a15e910cf8
...like "git clone host:foo/", even if it matches "repo foo/.*" NOTE: I expect a few more of these special cases to be found as time goes on and people find new ways to abuse the regex system, whether it is done intentionally or not. Anything not fixable by changing the config file will be fixed in the code asap. This one, for instance, seems fixable by using "foo/.+" instead of "foo/.*". But it actually isn't; the user can do "git clone host:foo//" and bypass that :( Still I suspect most situations will get an entry in the "then don't do that" file :) ---- patient: "doc, it hurts when I do this" doc: "then don't do that"
174 lines
6.4 KiB
Perl
Executable file
174 lines
6.4 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
# === auth-command ===
|
|
# the command that GL users actually run
|
|
|
|
# part of the gitolite (GL) suite
|
|
|
|
# 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
|
|
# ----------------------------------------------------------------------------
|
|
|
|
|
|
# these are set by the "rc" file
|
|
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $GL_ADMINDIR);
|
|
# and these are set by gitolite.pm
|
|
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT);
|
|
our %repos;
|
|
|
|
# the common setup module is in the same directory as this running program is
|
|
my $bindir = $0;
|
|
$bindir =~ s/\/[^\/]+$//;
|
|
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};
|
|
|
|
# add a custom path for git binaries, if specified
|
|
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# start...
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# 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!
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# sanity checks on SSH_ORIGINAL_COMMAND
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# SSH_ORIGINAL_COMMAND must exist; if not, we die with a nice message
|
|
unless ($ENV{SSH_ORIGINAL_COMMAND}) {
|
|
&report_basic($GL_ADMINDIR, $GL_CONF_COMPILED, $user);
|
|
exit 1;
|
|
}
|
|
|
|
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
|
|
my $repo_base_abs = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# get and set perms for actual repo created by wildcard-autoviv
|
|
# ----------------------------------------------------------------------------
|
|
|
|
my $CUSTOM_COMMANDS=qr/^\s*(expand|getperms|setperms)\s/;
|
|
|
|
# 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
|
|
|
|
if ($cmd =~ $CUSTOM_COMMANDS) {
|
|
my ($verb, $repo) = ($cmd =~ /^\s*(\S+)\s+\/?(.*?)(?:.git)?$/);
|
|
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);
|
|
}
|
|
elsif ($repo !~ $REPONAME_PATT and $verb eq 'expand') {
|
|
# with a wildcard, you can "expand" it to see what repos actually match
|
|
expand_wild($GL_CONF_COMPILED, $repo_base_abs, $repo, $user);
|
|
} else {
|
|
die "$cmd doesn't make sense to me\n";
|
|
}
|
|
exit 1;
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# normal (git) processing
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# split into command and arguments; the pattern 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
|
|
#
|
|
# keep in mind this is how git sends across the command:
|
|
# git-receive-pack 'reponame.git'
|
|
# including the single quotes
|
|
|
|
my ($verb, $repo) = ($cmd =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:.git)?'/);
|
|
die "bad command: $cmd. Make sure the repo name is exactly as in your config\n"
|
|
unless ( $verb and ( $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS )
|
|
and $repo and $repo =~ $REPONAME_PATT );
|
|
die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/;
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# first level permissions check
|
|
# ----------------------------------------------------------------------------
|
|
|
|
if ( -d "$repo_base_abs/$repo.git" ) {
|
|
# existing repo
|
|
my ($creater, $user_R, $user_W) = &repo_rights($repo_base_abs, $repo, $user);
|
|
my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $creater, $user_R, $user_W);
|
|
} else {
|
|
my $patt = &parse_acl($GL_CONF_COMPILED, $repo, $user, $user, $user);
|
|
# parse_acl returns "" if the repo was non-wildcard, or the pattern
|
|
# that matched if it was a wildcard
|
|
|
|
# auto-vivify new repo; 2 situations allow autoviv -- normal repos
|
|
# with W access (the old mode), and wildcard repos with C access
|
|
my $W_ok = $repos{$repo}{W}{$user} || $repos{$repo}{W}{'@all'};
|
|
my $C_ok = $repos{$repo}{C}{$user} || $repos{$repo}{C}{'@all'};
|
|
if ($W_ok and not $patt or $C_ok and $patt) {
|
|
wrap_chdir("$repo_base_abs");
|
|
# for wildcard repos, we also want to set the "creater"
|
|
new_repo($repo, "$GL_ADMINDIR/src/hooks", ( $patt ? $user : ""));
|
|
wrap_chdir($ENV{HOME});
|
|
}
|
|
}
|
|
|
|
# we know the user and repo; we just need to know what perm he's trying
|
|
my $perm = ($verb =~ $R_COMMANDS ? 'R' : 'W');
|
|
|
|
die "$perm access for $repo DENIED to $user\n"
|
|
unless $repos{$repo}{$perm}{$user}
|
|
or $repos{$repo}{$perm}{'@all'};
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# logging, timestamp. also setup env vars for later
|
|
# ----------------------------------------------------------------------------
|
|
|
|
# reponame
|
|
$ENV{GL_REPO}=$repo;
|
|
|
|
# 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;
|
|
|
|
# if log failure isn't important enough to block access, get rid of all the
|
|
# error checking
|
|
open my $log_fh, ">>", $ENV{GL_LOG}
|
|
or die "open log failed: $!\n";
|
|
print $log_fh "$ENV{GL_TS}\t$ENV{SSH_ORIGINAL_COMMAND}\t$user\n";
|
|
close $log_fh or die "close log failed: $!\n";
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# over to git now
|
|
# ----------------------------------------------------------------------------
|
|
|
|
$repo = "'$REPO_BASE/$repo.git'";
|
|
exec("git", "shell", "-c", "$verb $repo");
|