From 324d66c100a844c9554e676bb4ca18109f609b04 Mon Sep 17 00:00:00 2001 From: "R. Steve McKown" Date: Wed, 26 Sep 2012 16:10:54 -0600 Subject: [PATCH] repo_shell honors git ACLs * repo_shell uses git_acl.c * str_has_word returns C99 bool * git acl filename is set in repo_shell.cfg * git_acl() accepts const char* args * git_acl.h doesn't need to include stdbool.h * git_acl.c manages no default git acl filename * Use meaningful error message if permission denied due to ACL rule * repo_shell has -t mode to get git acl function --- Makefile | 2 +- README | 1 + git_acl.c | 12 +++++------ git_acl.h | 5 +---- repo_shell.c | 60 +++++++++++++++++++++++----------------------------- 5 files changed, 34 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index f625997..49f7334 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ PROGRAMS = $(MAIN) mystrtok_test acl_test all: $(MAIN) -repo_shell: repo_shell.c inih/ini.c mystrtok.c stra.c utility.c version.c +repo_shell: repo_shell.c inih/ini.c git_acl.c mystrtok.c stra.c utility.c version.c $(CC) -I inih $^ -o $@ mystrtok_test: mystrtok_test.c mystrtok.c utility.c diff --git a/README b/README index f46578e..91d9301 100644 --- a/README +++ b/README @@ -40,6 +40,7 @@ The /etc/repo_shell.cfg configuration file is straightforward: owner=repo git_root=/var/lib/git svn_root=/var/lib/svn/repositories + git_acl_file=/var/lib/git/.gitacls The owner field denotes the user that owns all repositories. The git_root and svn_root fields identify the path to the respective repositories. The latter diff --git a/git_acl.c b/git_acl.c index e077f78..6af2c2c 100644 --- a/git_acl.c +++ b/git_acl.c @@ -114,7 +114,7 @@ static acl_clear(acl_t *acl) stra_destroy(&acl->userids); } -static int str_has_word(const char* string, const char* word) +static bool str_has_word(const char* string, const char* word) { char *_s = xstrdup(string); char *s = _s; @@ -174,13 +174,11 @@ static int acl_handler(void* user, const char* section, const char* name, return 1; } -int git_acl(char *user, char *repo, char *file) +int git_acl(const char *user, const char *repo, const char *file) { acl_t acl; - if (!file || !*file) - file = GIT_ACL_FILE; - if (!user || !*user || !repo || !*repo) { + if (!file || !*file || !user || !*user || !repo || !*repo) { die("git_acl: invalid args user='%s', repo='%s', file='%s'", user, repo, file); } @@ -189,8 +187,8 @@ int git_acl(char *user, char *repo, char *file) set_lm_userid(NULL); lm_perms = PERMS_NOTFOUND; acl_init(&acl); - acl.user = user; - acl.repo = repo; + acl.user = (char*)user; + acl.repo = (char*)repo; stra_add(&acl.userids, acl.user); stra_add(&acl.repoids, acl.repo); diff --git a/git_acl.h b/git_acl.h index 80440cc..47c6aa8 100644 --- a/git_acl.h +++ b/git_acl.h @@ -18,7 +18,6 @@ #ifndef GIT_ACL_H #define GIT_ACL_H -#include #include #include #include @@ -32,8 +31,6 @@ #include "mystrtok.h" #include "stra.h" -#define GIT_ACL_FILE "git_acl.cfg" - typedef enum { PERMS_NOTFOUND = 0, PERMS_NONE, @@ -42,7 +39,7 @@ typedef enum { PERMS_COUNT } perms_t; -int git_acl(char *user, char *repo, char *file); +int git_acl(const char *user, const char *repo, const char *file); char *git_acl_last_repoid(); char *git_acl_last_userid(); int git_acl_last_perms(); diff --git a/repo_shell.c b/repo_shell.c index 6ec08b2..24eb6e2 100644 --- a/repo_shell.c +++ b/repo_shell.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -10,15 +11,16 @@ #include "ini.h" #include "utility.h" #include "version.h" +#include "git_acl.h" #define CFG_FILE "/etc/repo_shell.cfg" -#define GIT_ACL_FILE "git_acl.cfg" #define SHELL "/bin/bash" typedef struct { char *svn_root; char *git_root; char *owner; + char *git_acl_file; } cfg_t; static cfg_t cfg; @@ -76,40 +78,18 @@ static int check_ssh_interactive(uid_t uid) return 1; /* for now */ } -static int git_acl(const char *user, const char *repo) +/* Return true if the user's permissions >= those required */ +static bool git_check_access(const char *cmd, const char *repo, + const char *user) { - /* 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. + /* What access is required per the incoming command? Uploading commands + * require PERMS_READ_WRITE, while all other commands are assumed to need only + * PERMS_READ. */ -#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; + perms_t need = !strncmp(cmd, "git-upload", 10) ? PERMS_READ : + PERMS_READ_WRITE; + perms_t have = git_acl(user, repo, cfg.git_acl_file); + return have >= need; } static int do_git_cmd(const char *cmd, char *arg, char *user) @@ -125,7 +105,7 @@ static int do_git_cmd(const char *cmd, char *arg, char *user) change_user(cfg.owner); if (!git_check_access(cmd, arg, user)) - die("permission denied"); + die("insufficient ACL permissions"); nargv[0] = cmd; nargv[1] = add_prefix(cfg.git_root, arg); @@ -180,6 +160,8 @@ static int ini_handler(void* user, const char* section, const char* name, pconfig->git_root = xstrdup(value); else if (MATCH("core", "owner")) pconfig->owner = xstrdup(value); + else if (MATCH("core", "git_acl_file")) + pconfig->git_acl_file = xstrdup(value); else return 0; /* unknown section/name, error */ return 1; @@ -220,6 +202,16 @@ int main(int argc, char **argv) if (ini_parse(CFG_FILE, ini_handler, &cfg) < 0) die("cannot read config file %s", CFG_FILE); + if (argc == 4 && (!strcmp(argv[1], "-t") || + !strcmp(argv[1], "--test"))) { + perms_t p = git_acl(argv[2], argv[3], cfg.git_acl_file); + fprintf(stderr, + "user '%s' repo '%s' perms '%s'\n via userid '%s' repoid '%s'\n", + argv[2], argv[3], git_acl_perms_as_str(p), git_acl_last_userid(), + git_acl_last_repoid()); + return 0; + } + prog = xstrdup(argv[2]); if (!strncmp(prog, "git", 3) && isspace(prog[3])) /* Accept "git foo" as if the caller said "git-foo". */ -- 2.39.2