#!/usr/bin/perl -w # # $Id$ # ############################################################################ # The following preferences may be modified to match the local environment # ############################################################################ # Directory with the users data. $TMPDIR = '/tmp/asn1c-cgi-jail/'; $SUIDHelper = './asn1c-suid-helper'; $SkeletonsDir = '/usr/local/share/asn1c'; # Will be needed only once $CompilerLocation = '/usr/local/bin/asn1c'; # asn1c binary location $HashProgramPath = 'md5'; # Program to hash the input $DM = 0750; # Directory mode for all mkdirs. $MaxHistoryItems = 5; # Number of items in History $DynamicHistory = 'yes'; # Full/Short history $safeFilename = '^[a-z0-9_-]+[.a-z0-9_-]*$'; # Safe filename $ASN1C_Page = 'http://lionet.info/asn1c'; $HelpEmail = 'asn1c@lionet.info'; $defaultUserEmail = 'your@email'; $warn = '
";
###################################################
# The code below rarely requires any modification #
###################################################
my $redirect = ''; # No redirection by default
my $content = ''; # Default content is empty
use CGI qw/param cookie header upload escapeHTML/;
$|=1; # Enable AutoFlush (for older versions of Perl)
# If something goes wrong, this function is invoked to display the error message
sub bark($@) {
local $_ = join(" This page will disappear in 5 seconds."
. "";
$redirect = "";
goto PRINTOUT;
}
open(LOG, ">> $sessionDir/+logfile") or bark("Sandbox error: $!");
print LOG isoTime() . "\tIP=$ENV{REMOTE_ADDR}";
print LOG "\tEMAIL=$userEmail" if($userEmail ne $defaultUserEmail);
@gotSafeNames = ();
@gotNames = param('file');
if($#gotNames != -1 && $gotNames[0] ne "") {
$gotFile = param('file');
@gotFiles = upload('file');
} else {
@gotNames = ();
@gotFiles = ();
$gotFile = undef;
}
if($#gotNames == -1) {
my $text = param('text');
if($text) {
push(@gotNames, 'module.asn1');
}
}
# Make safe filenames
foreach my $fname (@gotNames) {
local $_ = $fname;
s/.*\///g; # Strip directory components
s/.*\\//g; # Strip directory components (DOS version)
s/^[.-]/_/g; # Don't allow filenames starting with a dot or a dash
s/[^._a-z0-9-]/_/gi;
if(!length($_)) {
print LOG "\n";
bark("Too strange filename: \"$fname\"");
}
$_ .= '.asn1' unless(/asn[1]{0,1}$/i);
@gotSafeNames = (@gotSafeNames, $_);
print LOG "\t" . $_;
}
#
# Save the files and start compilation process.
#
if($#gotSafeNames >= 0) {
$transactionDir = isoTime() . '--' . join("-", @gotSafeNames);
print LOG "\tDST=$transactionDir";
my $sandbox = $sessionDir . '/' . $transactionDir;
mkdir($sandbox, $DM) or bark($SandBoxInitFailed);
open(O, '> ' . $sandbox . '/+Names');
print O join("\n", @gotNames);
open(O, '> ' . $sandbox . '/+safeNames');
print O join("\n", @gotSafeNames);
for(my $i = 0; $i <= $#gotSafeNames; $i++) {
local $name = $gotSafeNames[$i];
open(O, '> ' . $sandbox . '/'. $name);
if($#gotFiles == -1) {
print O scalar(param('text'));
} else {
while(<$gotFile>) {
print O;
}
}
}
close(O);
my $inChDir = makeSessionDirName("/", $session) . $transactionDir;
my $options = '';
my $optDebugL = param('optDebugL');
my $optE = param('optE');
my $optEF = param('optEF');
my $optNT = param('optNT');
$options .= " -Wdebug-lexer"
if(defined($optDebugL) && $optDebugL eq "on");
$options .= " -E" if(defined($optE) && $optE eq "on");
$options .= " -EF" if(defined($optEF) && $optEF eq "on");
$options .= " -fnative-types" if(defined($optNT) && $optNT eq "on");
my $CompileASN = "$TMPDIR/bin/asn1c -v | sed -e 's/^/-- /'"
. " > $sandbox/+Compiler.Log 2>&1"
. "; $SUIDHelper $TMPDIR $inChDir $options @gotSafeNames "
. " >> $sandbox/+Compiler.Log 2>&1"
. "; echo \$? > $sandbox/+ExitCode";
system($CompileASN);
bark("Failed to initiate compilation process: $!")
if(!-r $sandbox . '/+ExitCode');
makeArchive($TMPDIR, $sandbox);
}
#print join(" "
. "Status: manual help requested '
. "
\n", @_);
$content = $warn . $_ . $unwarn;
goto PRINTOUT;
}
# Make the directory name containing session files for the given Session ID
sub makeSessionDirName($$) {
local $pfx = shift; # Prefix is the name of the top-level directory
local $sid = shift; # Session identifier (md5)
$pfx . '/sessions/' . $sid . '/';
}
# Create ISO 8601 time string: "YYYY-MM-DDThh:mm:ss"
my $cachedTime;
sub isoTime() {
return $cachedTime if $cachedTime;
local @tm = localtime(time);
$tm[5] += 1900;
$tm[4] += 1;
# Insert leading zeros
for(my $i = 0; $i < 5; $i++) {
$tm[$i] =~ s/^(.)$/0$1/;
}
$cachedTime = "$tm[5]-$tm[4]-$tm[3]T$tm[2]:$tm[1]:$tm[0]";
}
# Create the necessary environment for chrooting into.
sub prepareChrootEnvironment() {
return 1 if(-d $TMPDIR); # Envuronment already exists
mkdir $TMPDIR, $DM, or bark($OpEnvFailed, $!); # Global directory
mkdir $TMPDIR . 'sessions', $DM or bark($OpEnvFailed, $!); # sessions
mkdir $TMPDIR . 'bin', $DM or bark($OpEnvFailed, $!); # asn1c location
mkdir $TMPDIR . 'skeletons', $DM or bark($OpEnvFailed, $!); # asn1c data
if(-d '/lib') {
# Merge in dynamic libc
mkdir $TMPDIR . 'lib', $DM or bark($OpEnvFailed, $!);
system("cd $TMPDIR/lib && "
. "for i in"
. " /lib/ld-linux.*" # Linux ELF loader
. " /lib/libc.*" # Standard C library
. " /lib/libm.*" # Math library
. '; do ln $i; done');
} elsif(-d '/usr/lib') {
# There's no /lib on MacOS
mkdir $TMPDIR . 'usr', $DM or bark($OpEnvFailed, $!);
mkdir $TMPDIR . 'usr/lib', $DM or bark($OpEnvFailed, $!);
mkdir $TMPDIR . 'usr/lib/system', $DM or bark($OpEnvFailed, $!);
system("cd $TMPDIR/usr/lib && "
. "for i in"
. " /usr/lib/libc.*"
. " /usr/lib/libSystem.*"
. " /usr/lib/system/libmath*"
. " /usr/lib/dy*"
. '; do ln $i; done');
}
if(-d '/usr/libexec') {
# FreeBSD ELF loader
mkdir $TMPDIR . 'usr', $DM;
mkdir $TMPDIR . 'usr/libexec',$DM or bark($OpEnvFailed, $!);
system("cd $TMPDIR/usr/libexec && "
. 'for i in /usr/libexec/ld-elf.*; do ln $i; done');
}
system("cp $CompilerLocation $TMPDIR/bin 2>/dev/null") == 0
or bark($OpEnvFailed, $!);
system("cp -r $SkeletonsDir/* $TMPDIR/skeletons >/dev/null 2>&1") == 0
or bark($OpEnvFailed, $!);
return 1;
}
sub makeArchive($$) {
local $TMPDIR = shift;
local $sandbox = shift;
local $archName = $sandbox . '/+Archive.tgz';
if(! -f $archName) {
system("cd $sandbox && "
. "for i in ./*.[ch]; do if [ -L \$i ]; then"
. " cp $TMPDIR/skeletons/\$i \$i.-;"
. " mv \$i.- \$i;"
. " fi done && tar --dereference --ignore-failed-read --owner nobody --group nobody -zcf +tmp." . $$ . " *.[ch] Makefile* +Compiler.Log *.asn *.asn1"
. " && rm -f ./*.[ch] ./Makefile*"
. " && mv ./+tmp." . $$ . " $archName"
. " || rm -f ./+tmp." . $$);
undef unless -f $archName;
}
$archName;
}
my $EnvironmentSetOK = prepareChrootEnvironment();
#
# Record user's email.
#
$userEmail = cookie('userEmail');
$userEmail = $defaultUserEmail unless $userEmail;
$tmpEmail = param('email');
if(defined($tmpEmail)
&& $tmpEmail ne $userEmail) {
unless($tmpEmail =~ /^\s*([a-z0-9._+-]+@[a-z0-9.+-]+)\s*$/i) {
bark("Invalid email address: "
. "$tmpEmail");
}
$userEmail = $1;
local $ck = cookie(-name=>'userEmail',
-value=>$userEmail,
-path=>'/', -expires=>'+1d');
print "Set-Cookie: " . $ck . "\n";
}
#
# Check if full history requested.
#
$HistoryShow = cookie('HistoryShow');
$HistoryShow = '' unless $HistoryShow;
$tmpHSParam = param('history'); # Control cookie setting
if (defined($tmpHSParam)
&& $tmpHSParam ne $HistoryShow
&& $tmpHSParam =~ /^(full|short)$/) {
$HistoryShow = $tmpHSParam;
local $ck = cookie(-name=>'HistoryShow',
-value=>$HistoryShow,
-path=>'/', -expires=>'+1h');
print "Set-Cookie: " . $ck . "\n";
}
#
# Prepare the session and create the session directory.
# If session exists, perfom arguments checking and execute historic views.
#
$session = cookie('SessionID');
unless($session) {
$session = '';
open(R, '/dev/urandom')
or open(R, '/dev/random')
or bark($RandFailed);
read(R, $session, 16) == 16 or bark("Not enough randomness");
if($ENV{HTTP_USER_AGENT}) {
$session .= $ENV{HTTP_USER_AGENT}; # Add randomness
}
my $pid = open(R, "-|");
if($pid == 0) { # Child
open(W, "| $HashProgramPath") or die;
print W $session;
exit(0);
}
$session =
"
. "Results will be mailed to "
. "$userEmail shortly.
"
. "
\n", `env`);
$form =
"
\n";
} else {
$results = ""
. "Error during compilation: $ec
\n";
}
$allowFetchResults = $ec eq "0"
&& (-f $sessionDir . '/' . $trans . '/+Archive.tgz'
|| -f $sessionDir . '/' . $trans . '/Makefile.am.sample');
$results .= "
\n
"
. " by $eml,
"
. "expect results in a few hours.";
} else {
$results .= '
"
. ""
. ''
. ''
;
$atLeastOneError = 1;
}
}
$trColor = ' BGCOLOR=#f8f8f8';
$trColor = ' BGCOLOR=#d0ffe0' unless($CountHistoryItems);
$tNum = '' . $tNum . '' unless($CountHistoryItems);
$history .= ""
. " \n";
last if(++$CountHistoryItems >= $MaxHistoryItems
&& $HistoryShow ne 'full');
}
if($DynamicHistory eq 'yes') {
# [Un-]limit number of history items
$HistoryItemsHidden = 1 + $#transactions - $CountHistoryItems;
if($HistoryItemsHidden > 0) {
# Propose to expand the list.
local $item = 'item';
$HistoryItemsHidden == 1 or $item = 'items';
$history .= "$tNum "
. ""
. join(", ", @markedNames)
. " "
. ""
. " \n";
} elsif($HistoryShow eq "full" && $#transactions >= $MaxHistoryItems) {
# Propose to shorten the list.
local $item = 'item';
$MaxHistoryItems == 1 or $item = 'items';
$history .= ""
. ""
. "Show full history "
. "($HistoryItemsHidden hidden $item)"
. " \n";
}
}
if($history) {
$history = ""
. ""
. "Short history ($MaxHistoryItems $item)"
. " History
"
. ""
. " \n"
. "
"
. " \n"
. $history . "N "
. "Files processed "
. "Result "
. "
\n";
if($atLeastOneError) {
$history .= ""
. "Bottom line: ASN.1 compiler was unable to process some of the input files.
"
. "This is typically caused by syntax errors in the input files.\n"
. "Such errors are normally fixed by removing or adding a couple of characters in the ASN.1 module.
\n"
. "
Please consider clicking on an appropriate "Help me fix it!" link above.
\n"
. "An email will be sent to a person who will gladly fix the ASN.1 module for you. (The typical turn-around time is less than 24 hours.)\n"
. "
This is free, and highly advisable.\n"
. "Your request will help us make a better compiler!\n"
. "
Thank you."
. "";
}
}
unless($history) {
$history = ""
. "[compiled results will appear here]";
$histValign = 'center';
} else {
$histValign = 'top';
}
$content .=
"
";
$ua = $ENV{HTTP_USER_AGENT};
$ua =~ s/\\/\\\\/;
$ua =~ s/"/\\"/;
print LOG "\tUA=\"$ua\"";
print LOG "\n"; # Finalize logging record
PRINTOUT:
print header(-expires=>'-1y') unless($HTTPHeaderGenerated);
# If environment has never been set up completely, remove it.
if($EnvironmentSetOK != 1 && $TMPDIR ne "/") {
system("rm -rf $TMPDIR/ >/dev/null 2>&1");
}
print<\n"
. " ASN.1 Input
\n"
. "$form"
. "$history \n"
. " "
. "Privacy Note: this page is tailored "
. "to your browser using a cryprographically strong cookie. "
. "Other users will see their own (different) data. "
. "(Read more...)"
. ""
. "