#include "utility.h"
#include "version.h"
#include "git_acl.h"
+#include "stringutils.h"
-#define CFG_FILE "/etc/repo_shell.cfg"
+#define CFG_FILE "/etc/repo_shell.conf"
#define SHELL "/bin/bash"
+#define GIT_ACL_FILE ".gitacls"
+
+enum { REPO_UMASK = 027 };
typedef struct {
+ char *user;
char *svn_root;
char *git_root;
char *owner;
char *git_acl_file;
+ bool allow_interactive;
} cfg_t;
static cfg_t cfg;
-static uid_t user_uid(char *user)
+/* This is the function for which setuid root is needed for repo_shell */
+static void change_user(char *user)
{
struct passwd *pw = getpwnam(user);
if (!pw)
die("invalid user %s", user);
- return pw->pw_uid;
+ setgid(pw->pw_gid);
+ setuid(pw->pw_uid);
}
-static void change_user(char *user)
+/* Set the user and group permissions back to the requesting user */
+static void reset_user()
{
- /* This is the function for which setuid is required, as root */
- setuid(user_uid(user));
+ setgid(getgid());
+ setuid(getuid());
}
static char *dequote(char *arg)
return narg;
}
-static char *add_prefix(char *prefix, char* arg)
+static char *add_prefix(const char *prefix, const char* arg)
{
- char *narg = arg;
- int i;
+ char *narg;
+ int i;
if (arg && prefix && (i = strlen(prefix))) {
narg = xmalloc(sizeof(char *) * (i + strlen(arg) + 2));
return narg;
}
-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 */
-}
-
/* Return true if the user's permissions >= those required */
static bool git_check_access(const char *cmd, const char *repo,
const char *user)
die("bad command");
change_user(cfg.owner);
+ umask(REPO_UMASK);
if (!git_check_access(cmd, arg, user))
die("insufficient ACL permissions");
ret = execvp(nargv[0], (char *const *) nargv);
/* Code unreached if execv successful */
+ free((char*)nargv[1]);
free(narg);
return ret;
}
int ret;
change_user(cfg.owner);
+ umask(REPO_UMASK);
return execvp(svnserve_argv[0], (char *const *) svnserve_argv);
}
if (!strcmp(name, "svn_root"))
pconfig->svn_root = xstrdup(value);
- else if (!strcmp(name, "git_root"))
+ else if (!strcmp(name, "git_root")) {
pconfig->git_root = xstrdup(value);
- else if (!strcmp(name, "owner"))
+ pconfig->git_acl_file = add_prefix(value, GIT_ACL_FILE);
+ } else if (!strcmp(name, "owner"))
pconfig->owner = xstrdup(value);
- else if (!strcmp(name, "git_acl_file"))
- pconfig->git_acl_file = xstrdup(value);
+ else if (!strcmp(name, "allow_interactive"))
+ pconfig->allow_interactive = str_has_word(value, pconfig->user);
else
return 0; /* unknown section/name, error */
return 1;
struct commands *cmd;
int devnull_fd;
int count;
+ struct passwd *pw;
/*
* Always open file descriptors 0/1/2 to avoid clobbering files
die("opening /dev/null failed");
close (devnull_fd);
+ if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
+ fprintf(stderr, "%s is a replacement login shell.\n"
+ " May be ran from the command line with one of these options:\n"
+ " -h|--help this text\n"
+ " -v|--version program version string\n"
+ " -t|--test <user> <repo> test access\n"
+ " -d|--detail <user> <repo> test access, outputting more detail\n"
+ , argv[0]);
+ return 0;
+ }
+
if (argc == 2 && (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version"))) {
fprintf(stderr, "%s\n", version);
return 0;
}
+ pw = getpwuid(getuid());
+ cfg.user = xstrdup(pw->pw_name);
+ if (ini_parse(CFG_FILE, ini_handler, &cfg) < 0)
+ die("cannot read config file %s", CFG_FILE);
+
if (argc == 1) {
- if (!check_ssh_interactive(getuid()))
+ if (!cfg.allow_interactive) {
+ fprintf(stderr, "\n");
die("only repository access is allowed");
- setuid(getuid());
+ }
+ reset_user();
argv[0] = SHELL;
execvp(argv[0], (char *const *) argv);
return 1;
}
- if (ini_parse(CFG_FILE, ini_handler, &cfg) < 0)
- die("cannot read config file %s", CFG_FILE);
+ if ((!strcmp(argv[1], "-d") || !strcmp(argv[1], "--detail"))) {
+ perms_t p;
- if (argc == 4 && (!strcmp(argv[1], "-t") ||
- !strcmp(argv[1], "--test"))) {
- perms_t p = git_acl(argv[2], argv[3], cfg.git_acl_file);
+ if (argc !=4)
+ die("usage: %s -d|--detail <user> <repo>", argv[0]);
+ 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(),
return 0;
}
- 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':
+ if ((!strcmp(argv[1], "-t") || !strcmp(argv[1], "--test"))) {
+ perms_t p;
+
+ if (argc !=4)
+ die("usage: %s -t|--test <user> <repo>", argv[0]);
+ p = git_acl(argv[2], argv[3], cfg.git_acl_file);
+ printf("%s\n", git_acl_perms_as_str(p));
+ return 0;
+ }
+
+ if (argc == 3) {
+ /* argv[0] = repo_shell, argv[1] = -c, argv[2] = cmd
+ * cmd = "svnserve -t" or "git-xxx '/path/to/repo.git'"
+ */
+ 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;
+ if (strncmp(cmd->name, prog, len))
+ continue;
arg = NULL;
- break;
- case ' ':
- arg = prog + len + 1;
- break;
- default:
- continue;
+ switch (prog[len]) {
+ case '\0':
+ arg = NULL;
+ break;
+ case ' ':
+ arg = prog + len + 1;
+ break;
+ default:
+ continue;
+ }
+
+ exit(cmd->exec(cmd->name, arg, cfg.user));
}
-
- pw = getpwuid(getuid());
- exit(cmd->exec(cmd->name, arg, pw->pw_name));
}
- if (!check_ssh_interactive(getuid()))
+ if (!cfg.allow_interactive)
die("only repository access is allowed");
-
- setuid(getuid());
+ reset_user();
cd_to_homedir();
argv[0] = SHELL;
execvp(argv[0], (char *const *) argv);