(http) auth: handle REQUEST_URI and friends

TODO: if the verb doesn't actually contain "git-receive-pack", I am
assuming it is some sort of read.  The list in services[] in
http-backend.c does not seem to look like any other verb is a "write";
need to check this with someone.

For normal git commands:
  - PATH_INFO gives you the repo name
  - REQUEST_URI gives you the verb
  - we construct a fake SSH_ORIGINAL_COMMAND so the rest of the
    processing does not have to change

For our special commands:
  - PATH_INFO is actually the verb
  - QUERY_STRING has the parameters
  - we again fake out the SSH_ORIGINAL_COMMAND
  - we print the extra HTTP headers in anticipation of the actual output

Either way, we also fake out the SSH_CONNECTION so that the IP address
can get logged ok

And of course REMOTE_USER is now the incoming userid

Finally, at the end, we exec GIT_HTTP_BACKEND instead of the normal one
This commit is contained in:
Sitaram Chamarty 2010-09-05 18:43:21 +05:30
parent a9e9f98a7e
commit 52e0ed3488
2 changed files with 82 additions and 19 deletions

View file

@ -92,6 +92,22 @@ sub dbg {
}
}
my $http_headers_printed = 0;
sub print_http_headers {
my($code, $text) = @_;
return if $http_headers_printed++;
$code ||= 200;
$text ||= "OK - gitolite";
$|++;
print "Status: $code $text\r\n";
print "Expires: Fri, 01 Jan 1980 00:00:00 GMT\r\n";
print "Pragma: no-cache\r\n";
print "Cache-Control: no-cache, max-age=0, must-revalidate\r\n";
print "\r\n";
}
sub get_logfilename {
# this sub has a wee little side-effect; it sets $ENV{GL_TS}
my($template) = shift;

View file

@ -3,20 +3,29 @@
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:
# robustness:
# other notes:
# ----------------------------------------------------------------------------
# you: what's the invocation?
# me: Hail, O Lord Ganesha, destroyer of obsta...
# you: err hmm not *that* sort of invocation... I meant how does this program
# get invoked?
# me: oh hehe <hides sheepish grin>, ok here we go...
#
# ssh mode
# - started by sshd
# - one argument, the "user" name
# - one env var, SSH_ORIGINAL_COMMAND, containing the command
# - command typically: git-(receive|upload)-pack 'reponame(.git)?'
# - special gitolite commands: info, expand, (get|set)(perms|desc)
# - special non-gitolite commands: rsync, svnserve, htpasswd
# - other commands: anything in $GL_ADC_PATH if defined (see rc file)
#
# (smart) http mode
# - started by apache (httpd)
# - no arguments
# - REQUEST_URI contains verb and repo, REMOTE_USER contains username
# - REQUEST_URI looks like /path/reponame.git/(info/refs\?service=)?git-(receive|upload)-pack
# - no special processing commands currently handled
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# common definitions
@ -67,12 +76,43 @@ if (@ARGV and $ARGV[0] eq '-s') {
shift;
}
# no (more) arguments given? default user is $USER (fedorahosted works like
# this, and it is harmless for others)
@ARGV = ($ENV{USER}) unless @ARGV;
# ----------------------------------------------------------------------------
# set up SSH_ORIGINAL_COMMAND and SSH_CONNECTION in http mode
# ----------------------------------------------------------------------------
# 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!
# fake out SSH_ORIGINAL_COMMAND and SSH_CONNECTION so the rest of the code
# stays the same (except the exec at the end).
my $user;
if ($ENV{REQUEST_URI}) {
# these patterns indicate normal git usage; see "services[]" in
# http-backend.c for how I got that. Also note that "info" is overloaded;
# git uses "info/refs...", while gitolite uses "info" or "info?...". So
# there's a "/" after info in the list below
if ($ENV{PATH_INFO} =~ m(/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) {
my ($repo) = ($ENV{PATH_INFO} =~ m(^/(.*)\.git(?:/|$)));
my ($verb) = ($ENV{REQUEST_URI} =~ m((git-(?:receive|upload)-pack)));
print STDERR "(gitolite) no verb found in $ENV{REQUEST_URI}\n" unless $verb;
$verb ||= 'git-upload-pack';
$ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'";
} else {
# this is one of our custom commands; could be anything really,
# because of the adc feature
my ($verb) = ($ENV{PATH_INFO} =~ m(^/(\S+)));
my $args = $ENV{QUERY_STRING};
$args =~ s/\+/ /g;
$ENV{SSH_ORIGINAL_COMMAND} = $verb;
$ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args;
&print_http_headers(); # in preparation for the eventual output!
}
$ENV{SSH_CONNECTION} = "$ENV{REMOTE_ADDR} $ENV{REMOTE_PORT} $ENV{SERVER_ADDR} $ENV{SERVER_PORT}";
$user = $ENV{GL_USER} = $ENV{REMOTE_USER};
} else {
# no (more) arguments given in ssh mode? default user is $USER
# (fedorahosted works like this, and it is harmless for others)
@ARGV = ($ENV{USER}) unless @ARGV;
$user=$ENV{GL_USER}=shift;
}
# if there are any more arguments, they're a list of group names that the user
# is a member of
@ -212,6 +252,13 @@ die "$aa access for $repo DENIED to $user\n" unless $perm =~ /$aa/;
# over to git now
# ----------------------------------------------------------------------------
if ($ENV{REQUEST_URI}) {
&log_it($ENV{REQUEST_URI});
exec $ENV{GIT_HTTP_BACKEND};
# the GIT_HTTP_BACKEND env var should be set either by the rc file, or as
# a SetEnv in the apache config somewhere
}
&log_it();
$repo = "'$REPO_BASE/$repo.git'";