#! /usr/bin/perl -w # # "Shell" for a restricted account, limiting the available commands # This shell is particularly designed for Codendi restricted users: # it denies access to cvs repositories of projects the user is not member of. # # Based on work by Roland Mas, debian-sf (Sourceforge for Debian) # Inspired from the grap.c file in Sourceforge 2.5 # # Modified by N. Guerin for the Codendi project. # Copyright (c) Xerox Corporation, Codendi, 2005. All Rights Reserved # # # use strict ; use warnings ; use vars qw/ @allowed_options @allowed_commands $errmsg @cmd / ; use subs qw/ &reject / ; no locale ; @allowed_options = ('-c', '-e') ; @allowed_commands = ('cvs', 'scp', 'sftp') ; # Clean up our environment delete @ENV{qw(IFS CDPATH ENV BASH_ENV PATH)}; if ($#ARGV != 1) { if ($#ARGV < 1) { $errmsg = "Not enough arguments." ; } else { $errmsg = "Too many arguments." ; } &reject ; } if (scalar (grep { $_ eq $ARGV[0] } @allowed_options) == 0) { $errmsg = "Option not allowed." ; &reject ; } @cmd = split (/ +/, $ARGV[1]) ; if (scalar (grep { $_ eq $cmd[0] } @allowed_commands) == 0) { $errmsg = "Command not allowed." ; &reject ; } # Restricted Users # Deny access to CVS repositories if the user is not a member # of the project. # For info on CVS client/server protocol, see: # http://docs.freebsd.org/info/cvsclient/cvsclient.info.Requests.html if ($ARGV[1] eq "cvs server") { # Read the inital request from the CVS client. # If the CVS root specified in this request does not correspond to # a project the user is member of, then deny the access. my $req=''; while() { $req .= $_; if (/^[a-z]/) { # starts with lower case: end of initial request if (! /gzip-file-contents/ ) { # ...except in this case last; } } if (m|^Root.*/([^/]+)$|) { # Get CVS root. # Ex: 'Root /cvsroot/gpig' my $cvsroot=$1; chomp $cvsroot; # Now get the list of groups (thus projects) the user # is a member of. my $grouplist=`/usr/bin/id -nG`; chomp $grouplist; my @groups=split / /,$grouplist; my $allowed=0; my $group=''; foreach $group (@groups) { if ($group eq $cvsroot) { $allowed=1; last; } } if (!$allowed) { print "M Error: Access denied to this project\n"; exit 1; } } } # cleanup old files in tmp (files older than one day with same owner) my $username=`/usr/bin/whoami`; chomp $username; `find /tmp -name "cvssh.*" -atime +1 -user $username -exec /bin/rm {} \\;`; # small trick: re-inject initial request with upcoming data from STDIN # ... there should be a simpler way, though. my $timestamp=`/bin/date +%s%N`; chomp $timestamp; open TMP,"> /tmp/cvssh.$$.$timestamp"; print TMP $req; close TMP; system("cat /tmp/cvssh.$$.$timestamp - | cvs server"); unlink "/tmp/cvssh.$$"; } exec @cmd ; sub reject { print "This is a restricted account.\n" . "You cannot execute anything here.\n" . # $errmsg . "\n" . "Goodbye.\n" ; exit 1 ; }