2009-08-25 05:14:46 +02:00
|
|
|
#!/usr/bin/perl
|
2009-08-23 14:54:37 +02:00
|
|
|
|
|
|
|
use strict;
|
2009-08-25 05:14:46 +02:00
|
|
|
use warnings;
|
2009-08-23 14:54:37 +02:00
|
|
|
|
|
|
|
# === auth-command ===
|
|
|
|
# the command that GL users actually run
|
|
|
|
|
2009-08-26 02:47:27 +02:00
|
|
|
# part of the gitolite (GL) suite
|
2009-08-23 14:54:37 +02:00
|
|
|
|
|
|
|
# how run: via sshd, being listed in "command=" in ssh authkeys
|
|
|
|
# when: every login by a GL user
|
|
|
|
# input: $1 is GL username, plus $SSH_ORIGINAL_COMMAND
|
|
|
|
# output:
|
|
|
|
# security:
|
|
|
|
# - currently, we just make some basic checks, copied from gitosis
|
|
|
|
|
|
|
|
# robustness:
|
|
|
|
|
|
|
|
# other notes:
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# common definitions
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2009-12-04 05:21:22 +01:00
|
|
|
# these are set by the "rc" file
|
2010-02-05 11:30:47 +01:00
|
|
|
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS);
|
2009-12-04 05:21:22 +01:00
|
|
|
# and these are set by gitolite.pm
|
2010-01-29 10:09:03 +01:00
|
|
|
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT);
|
2009-08-23 14:54:37 +02:00
|
|
|
our %repos;
|
|
|
|
|
2009-10-25 03:59:52 +01:00
|
|
|
# the common setup module is in the same directory as this running program is
|
|
|
|
my $bindir = $0;
|
|
|
|
$bindir =~ s/\/[^\/]+$//;
|
2010-02-09 12:30:01 +01:00
|
|
|
$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//;
|
2009-10-25 03:59:52 +01:00
|
|
|
require "$bindir/gitolite.pm";
|
|
|
|
|
|
|
|
# ask where the rc file is, get it, and "do" it
|
|
|
|
&where_is_rc();
|
|
|
|
die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC};
|
2009-08-23 14:54:37 +02:00
|
|
|
|
2010-01-31 18:40:12 +01:00
|
|
|
# we need to pass GL_ADMINDIR and the bindir to the child hooks
|
2009-12-15 08:05:48 +01:00
|
|
|
$ENV{GL_ADMINDIR} = $GL_ADMINDIR;
|
|
|
|
$ENV{GL_BINDIR} = $bindir;
|
|
|
|
|
2009-10-13 06:32:45 +02:00
|
|
|
# add a custom path for git binaries, if specified
|
|
|
|
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
|
|
|
|
|
2009-12-15 11:41:21 +01:00
|
|
|
# set the umask before creating any files
|
|
|
|
umask($REPO_UMASK);
|
|
|
|
|
2010-02-04 10:12:10 +01:00
|
|
|
my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
|
|
|
|
2009-08-23 14:54:37 +02:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# start...
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2009-12-19 16:22:30 +01:00
|
|
|
# if the first argument is a "-s", this user is allowed to get a shell using
|
|
|
|
# this key
|
|
|
|
my $shell_allowed = 0;
|
|
|
|
if ($ARGV[0] eq '-s') {
|
|
|
|
$shell_allowed = 1;
|
|
|
|
shift;
|
|
|
|
}
|
|
|
|
|
2009-08-23 14:54:37 +02:00
|
|
|
# first, fix the biggest gripe I have with gitosis, a 1-line change
|
|
|
|
my $user=$ENV{GL_USER}=shift; # there; now that's available everywhere!
|
|
|
|
|
2010-02-01 12:24:39 +01:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# logging, timestamp env vars
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# timestamp
|
|
|
|
my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5];
|
|
|
|
$y += 1900; $m++; # usual adjustments
|
|
|
|
for ($s, $min, $h, $d, $m) {
|
|
|
|
$_ = "0$_" if $_ < 10;
|
|
|
|
}
|
|
|
|
$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s";
|
|
|
|
|
|
|
|
# substitute template parameters and set the logfile name
|
|
|
|
$GL_LOGT =~ s/%y/$y/g;
|
|
|
|
$GL_LOGT =~ s/%m/$m/g;
|
|
|
|
$GL_LOGT =~ s/%d/$d/g;
|
|
|
|
$ENV{GL_LOG} = $GL_LOGT;
|
|
|
|
|
2009-08-23 14:54:37 +02:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# sanity checks on SSH_ORIGINAL_COMMAND
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2010-02-01 07:06:24 +01:00
|
|
|
# no SSH_ORIGINAL_COMMAND given...
|
2009-10-28 09:03:24 +01:00
|
|
|
unless ($ENV{SSH_ORIGINAL_COMMAND}) {
|
2010-02-01 07:06:24 +01:00
|
|
|
# if the user is allowed to use a shell, give him one
|
2009-12-19 16:22:30 +01:00
|
|
|
if ($shell_allowed) {
|
|
|
|
my $shell = $ENV{SHELL};
|
|
|
|
$shell =~ s/.*\//-/; # change "/bin/bash" to "-bash"
|
|
|
|
exec { $ENV{SHELL} } $shell;
|
|
|
|
}
|
2010-02-01 07:06:24 +01:00
|
|
|
# otherwise, pretend he typed in "info" and carry on...
|
|
|
|
$ENV{SSH_ORIGINAL_COMMAND} = 'info';
|
2009-10-28 09:03:24 +01:00
|
|
|
}
|
|
|
|
|
2009-12-06 10:09:40 +01:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# get and set perms for actual repo created by wildcard-autoviv
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2010-02-18 14:50:46 +01:00
|
|
|
my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\b/;
|
2009-12-06 10:09:40 +01:00
|
|
|
|
|
|
|
# note that all the subs called here chdir somewhere else and do not come
|
|
|
|
# back; they all blithely take advantage of the fact that processing custom
|
|
|
|
# commands is sort of a dead end for normal (git) processing
|
|
|
|
|
2010-02-04 10:12:10 +01:00
|
|
|
if ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) {
|
2010-02-05 11:30:47 +01:00
|
|
|
die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS;
|
2010-02-04 10:12:10 +01:00
|
|
|
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
2010-03-01 16:02:54 +01:00
|
|
|
my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+'?\/?(.*?)(?:\.git)?'?)?$/);
|
2010-02-18 14:50:46 +01:00
|
|
|
# deal with "no argument" cases
|
2010-02-26 15:55:28 +01:00
|
|
|
$verb eq 'expand' ? $repo = '^' : die "$verb needs an argument\n" unless $repo;
|
2009-12-06 10:09:40 +01:00
|
|
|
if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) {
|
|
|
|
# with an actual reponame, you can "getperms" or "setperms"
|
|
|
|
get_set_perms($repo_base_abs, $repo, $verb, $user);
|
|
|
|
}
|
2010-02-04 22:40:13 +01:00
|
|
|
elsif ($repo =~ $REPONAME_PATT and $verb =~ /(get|set)desc/) {
|
|
|
|
# with an actual reponame, you can "getdesc" or "setdesc"
|
|
|
|
get_set_desc($repo_base_abs, $repo, $verb, $user);
|
2009-12-06 10:09:40 +01:00
|
|
|
}
|
2009-12-21 13:19:21 +01:00
|
|
|
elsif ($verb eq 'expand') {
|
2009-12-06 10:09:40 +01:00
|
|
|
# with a wildcard, you can "expand" it to see what repos actually match
|
2010-01-29 10:09:03 +01:00
|
|
|
die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT;
|
2010-04-16 15:49:50 +02:00
|
|
|
expand_wild($GL_ADMINDIR, $GL_CONF_COMPILED, $repo_base_abs, $repo, $user);
|
2009-12-06 10:09:40 +01:00
|
|
|
} else {
|
|
|
|
die "$cmd doesn't make sense to me\n";
|
|
|
|
}
|
2010-01-08 13:05:11 +01:00
|
|
|
exit 0;
|
2009-12-06 10:09:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
2010-02-01 11:07:35 +01:00
|
|
|
# non-git commands
|
2009-12-06 10:09:40 +01:00
|
|
|
# ----------------------------------------------------------------------------
|
2009-08-23 14:54:37 +02:00
|
|
|
|
2010-02-01 11:07:35 +01:00
|
|
|
# if the command does NOT fit the pattern of a normal git command, send it off
|
|
|
|
# somewhere else...
|
2009-08-23 14:54:37 +02:00
|
|
|
|
2010-02-01 11:07:35 +01:00
|
|
|
# side notes on detecting a normal git command: the pattern we check allows
|
|
|
|
# old style as well as new style ("git-subcommand arg" or "git subcommand
|
|
|
|
# arg"), just like gitosis does, although I'm not sure how necessary that is.
|
|
|
|
# Currently, this is how git sends across the command (including the single
|
|
|
|
# quotes):
|
|
|
|
# git-receive-pack 'reponame.git'
|
2009-08-23 14:54:37 +02:00
|
|
|
|
2010-02-01 11:07:35 +01:00
|
|
|
my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
|
2009-12-19 16:22:30 +01:00
|
|
|
unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) {
|
2010-02-01 11:07:35 +01:00
|
|
|
# ok, it's not a normal git command; call the special command helper
|
2010-02-04 10:46:47 +01:00
|
|
|
&special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE);
|
2010-02-01 11:07:35 +01:00
|
|
|
exit;
|
2009-12-19 16:22:30 +01:00
|
|
|
}
|
2009-12-11 17:07:33 +01:00
|
|
|
die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/;
|
2009-12-13 08:13:44 +01:00
|
|
|
die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./;
|
2009-08-23 14:54:37 +02:00
|
|
|
|
2010-02-04 10:12:10 +01:00
|
|
|
# reponame
|
|
|
|
$ENV{GL_REPO}=$repo;
|
2009-08-23 14:54:37 +02:00
|
|
|
|
2010-02-01 11:07:35 +01:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# the real git commands (git-receive-pack, etc...)
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2009-08-23 14:54:37 +02:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# first level permissions check
|
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2009-12-05 18:09:56 +01:00
|
|
|
if ( -d "$repo_base_abs/$repo.git" ) {
|
|
|
|
# existing repo
|
|
|
|
my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user);
|
2009-12-21 13:19:21 +01:00
|
|
|
&parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W);
|
2009-12-05 18:09:56 +01:00
|
|
|
} else {
|
2010-03-27 18:01:10 +01:00
|
|
|
&parse_acl($GL_CONF_COMPILED, $repo, $user, "NOBODY", "NOBODY");
|
2009-12-21 13:19:21 +01:00
|
|
|
|
2010-02-05 11:30:47 +01:00
|
|
|
# auto-vivify new repo if you have C access (and wildrepos is on)
|
|
|
|
if ( $GL_WILDREPOS and $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'} ) {
|
2009-12-05 18:09:56 +01:00
|
|
|
wrap_chdir("$repo_base_abs");
|
2010-02-09 15:07:37 +01:00
|
|
|
new_repo($repo, "$GL_ADMINDIR/hooks/common", $user);
|
2009-12-05 18:09:56 +01:00
|
|
|
wrap_chdir($ENV{HOME});
|
auth, gitolite.pm: do not leak info about repo existence
All this is about a user trying to look if a repo exists or not, when he
does not have any access to that repo. Ideally, "repo does not exist"
should be indistinguishable from "you dont have perms to that repo".
(1) if $GL_WILDREPOS is not set, you either get a permissions error, or
a "$repo not found in compiled config" death. Fixed.
(2) if $GL_WILDREPOS is set, you either get either a permissions error,
or a "$repo has no matches" death. Fixed.
(3) The following combination leaks info about repo existence:
- actual repo doesn't exist
- spying user don't have C perms
- repo patt doesn't contain CREATER
- RW+ = CREATER is specified (as is normal)
In such case, the "convenience copy" of the ACL that parse_acl
makes, coupled with substituting CREATER for the invoking user means
$repos{$actual_repo} has RW+ for the spying user. This means the
access denied doesn't happen, and control passes to git, which
promptly expresses it unhappiness and angst over being given a repo
that 'does not appear to be a git repository'
This doesn't happen if all those conditions are not met:
- if repo exists, CREATER is set to the real creater, so RW+ =
CREATER does not gain spying user anything
- if spying user has C perms it just gets created, because he has
rights. This is also info leak but we can't prevent it; tighten
the config (maybe by including CREATER in repo pattern) if this
is not wanted
- if repo patt contains CREATER it will never match someone else's
repo anyway!
2010-03-28 20:14:37 +02:00
|
|
|
} else {
|
|
|
|
# repo didn't exist, and you didn't have perms to create it. Delete
|
|
|
|
# the "convenience" copy of the ACL that parse_acl makes for us
|
|
|
|
delete $repos{$repo};
|
2009-12-05 18:09:56 +01:00
|
|
|
}
|
|
|
|
}
|
2009-12-04 05:21:22 +01:00
|
|
|
|
2009-08-25 05:51:07 +02:00
|
|
|
# we know the user and repo; we just need to know what perm he's trying
|
|
|
|
my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W');
|
|
|
|
|
2009-11-22 05:51:22 +01:00
|
|
|
die "$perm access for $repo DENIED to $user\n"
|
2009-08-27 09:44:47 +02:00
|
|
|
unless $repos{$repo}{$perm}{$user}
|
2010-03-23 17:50:34 +01:00
|
|
|
or $repos{'@all'}{$perm}{$user} # new: access to @all repos
|
2009-08-27 09:44:47 +02:00
|
|
|
or $repos{$repo}{$perm}{'@all'};
|
2009-08-23 14:54:37 +02:00
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------
|
2010-02-01 12:24:39 +01:00
|
|
|
# over to git now
|
2009-08-23 14:54:37 +02:00
|
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
2010-01-31 18:40:12 +01:00
|
|
|
&log_it("$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n");
|
2009-08-23 14:54:37 +02:00
|
|
|
|
|
|
|
$repo = "'$REPO_BASE/$repo.git'";
|
|
|
|
exec("git", "shell", "-c", "$verb $repo");
|