gitolite/src/gl-auth-command

229 lines
8.6 KiB
Plaintext
Raw Normal View History

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:
# robustness:
# other notes:
# ----------------------------------------------------------------------------
# common definitions
# ----------------------------------------------------------------------------
# these are set by the "rc" file
gitweb/daemon now work for wild repos also (thanks to Kevin Fleming for the need/use case) TODO: tests TODO: proper documentation; meanwhile, just read this: - you can give gitweb and daemon read rights to wild card repos also, and it'll all just work -- when a new repo is 'C'reated, it'll pick up those rights etc - you can assign descriptions (and owners) to individual repos as before, except now you can assign them to repos that actually were created from wild card patterns. So for example, you can define rules for repo foo/..* and then assign descriptions like foo/repo1 = "repo one" foo/repo2 = "repo two" foo/dil "scott" = "scott's dilbert repo" However, this only works for repos that already exist, and only when you push the admin repo. Thumb rule: have the user create his wild repo, *then* add and push the admin config file with the description. Not the other way around. implementation notes: - wildcard support for git config revamped, refactored... it's not just git config that needs wildcard support. daemon and gitweb access also will be needing it soon, so we start by factoring out the part that finds the "pattern" given a "real" repo name. - GL_NO_DAEMON_NO_GITWEB now gates more than just those two things; see doc/big-config.mkd for details - we trawl through $GL_REPO_BASE_ABS *once* only, collecting repo names and tying them to either the same name or to a wild pattern that the repo name was created from - nice little subs to setup gitweb, daemon, and git config - god bless $GL_REPOPATT and the day I decided to set that env var whenever a user hits a wild repo in any way :-) - the code in gl-compile-conf is very simple now. Much nicer than before
2010-07-16 19:31:34 +02:00
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE);
# and these are set by gitolite.pm
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT);
2009-08-23 14:54:37 +02:00
our %repos;
our %groups;
our %repo_config;
2009-08-23 14:54:37 +02: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 =~ /^\//;
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
# we need to pass GL_ADMINDIR and the bindir to the child hooks
$ENV{GL_ADMINDIR} = $GL_ADMINDIR;
$ENV{GL_BINDIR} = $bindir;
# add a custom path for git binaries, if specified
$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
# set default permission of wildcard repositories
$ENV{GL_WILDREPOS_DEFPERMS} = $GL_WILDREPOS_DEFPERMS if $GL_WILDREPOS_DEFPERMS;
# set the umask before creating any files
umask($REPO_UMASK);
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
2009-08-23 14:54:37 +02:00
# ----------------------------------------------------------------------------
# start...
# ----------------------------------------------------------------------------
# if the first argument is a "-s", this user is allowed to get a shell using
# this key
my $shell_allowed = 0;
if (@ARGV and $ARGV[0] eq '-s') {
$shell_allowed = 1;
shift;
}
# no (more) arguments given? default user is $USER (fedorahosted works like
# this, and it is harmless for others)
@ARGV = ($ENV{USER}) unless @ARGV;
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!
# if there are any more arguments, they're a list of group names that the user
# is a member of
$ENV{GL_GROUP_LIST} = join(" ", @ARGV) if @ARGV;
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
# ----------------------------------------------------------------------------
# no SSH_ORIGINAL_COMMAND given...
unless ($ENV{SSH_ORIGINAL_COMMAND}) {
# if the user is allowed to use a shell, give him one
if ($shell_allowed) {
my $shell = $ENV{SHELL};
$shell =~ s/.*\//-/; # change "/bin/bash" to "-bash"
&log_it($shell);
exec { $ENV{SHELL} } $shell;
}
# otherwise, pretend he typed in "info" and carry on...
$ENV{SSH_ORIGINAL_COMMAND} = 'info';
}
# ----------------------------------------------------------------------------
# slave mode should not do much
# ----------------------------------------------------------------------------
die "server is in slave mode; you can only fetch\n"
if ($GL_SLAVE_MODE and $ENV{SSH_ORIGINAL_COMMAND} !~ /^(info|expand|get|git-upload-)/);
# ----------------------------------------------------------------------------
# admin defined commands
# ----------------------------------------------------------------------------
# please see doc/admin-defined-commands.mkd for details
if ($GL_ADC_PATH and -d $GL_ADC_PATH) {
my ($cmd, @args) = split ' ', $ENV{SSH_ORIGINAL_COMMAND};
if (-x "$GL_ADC_PATH/$cmd") {
# yes this is rather strict, sorry.
do { die "I don't like $_\n" unless $_ =~ $REPOPATT_PATT } for ($cmd, @args);
&log_it("$GL_ADC_PATH/$ENV{SSH_ORIGINAL_COMMAND}");
exec("$GL_ADC_PATH/$cmd", @args);
}
}
# ----------------------------------------------------------------------------
# get and set perms for actual repo created by wildcard-autoviv
# ----------------------------------------------------------------------------
my $CUSTOM_COMMANDS=qr/^\s*(expand|(get|set)(perms|desc))\b/;
# 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 ($ENV{SSH_ORIGINAL_COMMAND} =~ $CUSTOM_COMMANDS) {
die "wildrepos disabled, sorry\n" unless $GL_WILDREPOS;
my $cmd = $ENV{SSH_ORIGINAL_COMMAND};
my ($verb, $repo) = ($cmd =~ /^\s*(\S+)(?:\s+'?\/?(.*?)(?:\.git)?'?)?$/);
# deal with "no argument" cases
$verb eq 'expand' ? $repo = '^' : die "$verb needs an argument\n" unless $repo;
if ($repo =~ $REPONAME_PATT and $verb =~ /getperms|setperms/) {
# with an actual reponame, you can "getperms" or "setperms"
get_set_perms($repo, $verb, $user);
}
elsif ($repo =~ $REPONAME_PATT and $verb =~ /(get|set)desc/) {
# with an actual reponame, you can "getdesc" or "setdesc"
get_set_desc($repo, $verb, $user);
}
elsif ($verb eq 'expand') {
# with a wildcard, you can "expand" it to see what repos actually match
die "$repo has invalid characters" unless "x$repo" =~ $REPOPATT_PATT;
expand_wild($GL_ADMINDIR, $GL_CONF_COMPILED, $repo, $user);
} else {
die "$cmd doesn't make sense to me\n";
}
exit 0;
}
# ----------------------------------------------------------------------------
# non-git commands
# ----------------------------------------------------------------------------
2009-08-23 14:54:37 +02: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
# 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
2010-05-21 18:02:33 +02:00
# arg"). 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
my ($verb, $repo) = ($ENV{SSH_ORIGINAL_COMMAND} =~ /^\s*(git\s+\S+|\S+)\s+'\/?(.*?)(?:\.git)?'/);
unless ( $verb and ( $verb eq 'git-init' or $verb =~ $R_COMMANDS or $verb =~ $W_COMMANDS ) and $repo and $repo =~ $REPONAME_PATT ) {
# ok, it's not a normal git command; call the special command helper
&special_cmd ($GL_ADMINDIR, $GL_CONF_COMPILED, $shell_allowed, $RSYNC_BASE, $HTPASSWD_FILE, $SVNSERVE);
exit;
}
die "$repo ends with a slash; I don't like that\n" if $repo =~ /\/$/;
die "$repo has two consecutive periods; I don't like that\n" if $repo =~ /\.\./;
2009-08-23 14:54:37 +02:00
# reponame
$ENV{GL_REPO}=$repo;
2009-08-23 14:54:37 +02:00
# ----------------------------------------------------------------------------
# the real git commands (git-receive-pack, etc...)
# ----------------------------------------------------------------------------
2009-08-23 14:54:37 +02:00
# ----------------------------------------------------------------------------
# first level permissions check
# ----------------------------------------------------------------------------
my ($perm, $creator, $wild) = &repo_rights($repo);
if ($perm =~ /C/) {
# it was missing, and you have create perms
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
new_repo($repo, "$GL_ADMINDIR/hooks/common", $user);
&setup_repo_configs($repo, \%repo_config);
&setup_daemon_access($repo);
system("echo $repo.git >> $PROJECTS_LIST") if &setup_gitweb_access($repo, '', '');
wrap_chdir($ENV{HOME});
}
# we know the user and repo; we just need to know what perm he's trying
# aa == attempted access
my $aa = ($verb =~ $R_COMMANDS ? 'R' : 'W');
die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/;
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
# ----------------------------------------------------------------------------
&log_it();
2009-08-23 14:54:37 +02:00
$repo = "'$REPO_BASE/$repo.git'";
exec("git", "shell", "-c", "$verb $repo") unless $verb eq 'git-init';