#
# autopsy gui server
# Autopsy Forensic Browser
#
#
# This file requires The Sleuth Kit 
#    www.sleuthkit.org
#
# version 1.71+
# Brian Carrier [carrier@sleuthkit.org]
# Copyright (c) 2003 by Brian Carrier.  All rights reserved
#
# version 1.5, 1.6, 1.7
# Copyright (c) 2001-2003 by Brian Carrier, @stake Inc.  All rights reserved
#
# version 1.0
# Brian Carrier [carrier@cerias.purdue.edu]
# Copyright (c) 2001 by Brian Carrier.  All rights reserved
#   
#
# This file is part of the Autopsy Forensic Browser (Autopsy)
#
# Autopsy is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Autopsy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Autopsy; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# THIS SOFTWARE IS NOT AFFILIATED WITH PURDUE UNIVERSITY OR THE CENTER FOR
# EDUCATION IN INFORMATION ASSURANCE AND SECURITY (CERIAS) AND THEY BEAR
# NO RESPONSIBILITY FOR ITS USE OR MISUSE.
#
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE.
# IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, LOSS OF USE, DATA, OR PROFITS OR
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

#
# refer to Security Considerations in README for a description of the 
# cookie authentication
#

require 5.002;

use autopsyfunc;	
use strict;
use Socket;

require 'conf.pl';
require 'define.pl';

# Import variables from conf.pl
use vars '$PROGNAME', '$TASKDIR';
use vars '$LOCKDIR', '$INSTALLDIR', '$PICTDIR';
use vars '$SANITIZE_TAG', '$SANITIZE_PICT';
use vars '$USE_STIMEOUT', '$STIMEOUT', '$CTIMEOUT';
use vars '$USE_COOKIE', '$USE_LOG';
use vars '$SAVE_COOKIE', '$STRINGS_EXE', '$GREP_EXE';
use vars '$AUT_HOME_PAGE';
use vars '$HTTP_NL', '$NSRLDB';


# remove environment stuff that we don't need and that could be insecure
$ENV{PATH} = '';
delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
$| = 1;

sub usage {
    print "usage: $0 [-C] [-d evid_locker] [port [remoteaddr]]\n";
	exit 1;
}

if (scalar(@ARGV) > 5) {
	usage();
}

# Were options given?
while ((scalar (@ARGV) > 0) && ($ARGV[0] =~ /^-/)) {
	my $f = shift;

	# Evidence Locker
	if ($f eq '-d') {
		if (scalar (@ARGV) == 0) {
			print "Missing Directory\n";
			usage();
		}

		my $d = shift;
		# We need to do this for the tainting
		# We don't need to check for special characters in this case because
		# all commands will be run with the same permissions as the
		# original user.  We will check for the obvious ';' though
		if ($d =~ /;/) {
			print "Illegal argument\n";
			exit (1);
		}

		# If the path is relative, autopsyfunc will get screwed up when
		# this is run from a directory other than where autopsyfunc is
		# so force full paths
		elsif ($d !~ /^\//) {
			print "The evidence locker must be full path (i.e. begin with /)\n";
			exit(1);
		}
		elsif ($d =~ /(.*)/) {
			$LOCKDIR = $1;
		}
	}

	# Force no cookie
	elsif ($f eq '-C') {
		$USE_COOKIE = 0;
	}
	else {
		print "Invalid flag: $f\n";
		usage();
	}
}


# Port Number
my $port;
if (scalar (@ARGV) > 0) {
	$port = shift;
	if ($port =~ /(\d+)/) {
		$port = $1;
	}
	else {
		print "invalid port: $port\n";
		usage();
	}
}
else {
	$port = 9999;
}

# remote address
my $rema;
if (scalar (@ARGV) > 0) {
	$rema = shift;
}
else {
	$rema = 'localhost';
}


# Get remote address
my @acl_addr;		# Array of host addresses
my $hn;				# Host name
my $tmp;
if ($rema =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/) {
	$acl_addr[0] = pack('C4', ($1, $2, $3, $4));
	$hn = $rema;
}
else {
	($hn, $tmp, $tmp, $tmp, @acl_addr) = gethostbyname($rema);
	unless (defined $tmp) {
		print "Host not found: $rema\n";
		usage();
	}
}

# Determine the address that will be used to access this server
my $lclhost;
my @ta = unpack('C4', $acl_addr[0]);

# If we are being accessed by localhost, we need that and not the hostname
if (($ta[0] == 127) && ($ta[1] == 0) &&
  ($ta[2] == 0) && ($ta[3] == 1)) {
	$lclhost = "localhost";
}
else {
	$lclhost = `/bin/hostname`;
	chop $lclhost;
}


# Verify the variables defined in the configuration files
check_vars();


# Make sure TASKDIR ends with '/'
if ($TASKDIR !~ /.*?\/$/) {
    $TASKDIR .= '/';
}      

# Make sure LOCKDIR ends with '/'
if ($LOCKDIR !~ /.*?\/$/) {
	$LOCKDIR .= '/';     
}


#
# Verify that all of the required executables exist
#


check_tools();

my $date = localtime;
print <<EOF;
============================================================================

                       Autopsy Forensic Browser 
                             ver $VER 

============================================================================

Evidence Locker: $LOCKDIR
Start Time: $date

EOF



# Setup socket
my $proto = getprotobyname('tcp');
socket (Server, PF_INET, SOCK_STREAM, $proto) 
  or die "Create socket failed: $!";

setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, 1) 
  or die "Setsockopt failed: $!";

setsockopt(Server, SOL_SOCKET, SO_KEEPALIVE, 1) 
  or die "Setsockopt failed: $!";

bind(Server, sockaddr_in($port, INADDR_ANY)) 
  or die "Bind to port $port failed: $!";

listen(Server, SOMAXCONN) 
  or die "Listen to socket failed: $!";


my $magic;		# magic authentication cookie
my $cook_file;
my $cookie_url = "";

if  ($USE_COOKIE == 1) {

	# Try for a real random device, or use rand if all else fails
	if (-e "/dev/urandom") {
		my $r;
		open RAND, "</dev/urandom" or die "can not open /dev/urandom";
		read RAND, $r, 4;
		$magic = unpack "I", $r;
		read RAND, $r, 4;
		$magic .= unpack "I", $r;
		close RAND;
	}
	else {
		$magic = int(rand 0xffffffff).int(rand 0xffffffff);
	}

	$cookie_url = "$magic/";

	# Save to file in case the stdout gets overwritten
	if ($SAVE_COOKIE == 1) {
		$cook_file = "$LOCKDIR/.$port.cookie";
		open COOK, ">$cook_file" or
		  die "can not open $cook_file";
		chmod 0600, "$cook_file";
		print COOK "$magic\n";
		close COOK;
	}
} 


print "Paste this as your browser URL on $rema:\n";
print "\thttp://$lclhost:${port}/${cookie_url}${PROGNAME}\n";
print "\nKeep this process running and use <ctrl-c> to exit\n";

log_session_info("Starting session on port $port and $hn\n");

# Set the server alarm
$SIG{ALRM} = \&sig_alarm_server;
$SIG{INT} = \&sig_close;
$SIG{CHLD} = sub {
	my $p = wait;
};

# Wait for Connections
while (1) {

	alarm ($STIMEOUT) if ($USE_STIMEOUT == 1);

	my $raddr = accept(CLIENT, Server); 
	next unless ($raddr);
	my ($rport, $riaddr) = sockaddr_in($raddr);
	my $pid;

	if (0 == ($pid = fork)) {
		open(STDOUT, ">&CLIENT") or die "Can't dup client to stdout";
		open(STDERR, ">&CLIENT") or die "Can't dup client to stdout";
		open(STDIN, "<&CLIENT") or die "Can't dup client to stdin";
		$| = 1;

		my @rip = unpack('C4', $riaddr);

		# Check ACL
		foreach $tmp (@acl_addr) {
			if ($tmp eq $riaddr) {
				spawn_cli($riaddr);
				close CLIENT;
				exit 0;
			}
		}

		forbid ();
		log_session_info("ERROR: Unauthorized Connection from:".
		   "$rip[0].$rip[1].$rip[2].$rip[3]\n");

		close CLIENT;
		exit 1;
	}
	else {
		close CLIENT;
	}
}


# Error messages
sub forbid 
{
	print "HTTP/1.0 403 Forbidden${HTTP_NL}".
	  "Content-type: text/html${HTTP_NL}${HTTP_NL}".
	  "<HTML><CENTER><BR>\n".
	  "<H2>Access Denied</H2></CENTER></HTML>${HTTP_NL}${HTTP_NL}${HTTP_NL}";
	return;
}

sub bad_req 
{
	print "HTTP/1.0 404 Bad Request${HTTP_NL}".
	  "Content-type: text/html${HTTP_NL}${HTTP_NL}".
	  "<HTML><CENTER><BR>\n".
	  "<H2>Invalid Location: <TT>".shift()."</TT></H2></CENTER></HTML>".
	  "${HTTP_NL}${HTTP_NL}${HTTP_NL}";
	return;
}


sub sterile
{
	my $url = shift();
	my $lurl = $url;
	$lurl =~ tr/[A-Z]/[a-z]/;

	print "HTTP/1.0 200 OK${HTTP_NL}";
	if (($lurl =~ /.jpg/i) || ($lurl =~ /.jpeg/i) || ($lurl =~ /.gif/i) ||
	  ($lurl =~ /.png/i) || ($lurl =~ /.bmp/i)) {

		open PICT, "<$PICTDIR/$SANITIZE_PICT" or  
		  die "can not open $PICTDIR/$SANITIZE_PICT";

		print "Content-type: image/jpeg${HTTP_NL}${HTTP_NL}";
		while (<PICT>) {
			print "$_";
		}
		close (PICT);
	}
	else {
		$url =~ tr/\+/ /;
		$url =~ s/%([a-f0-9][a-f0-9])/chr( hex( $1 ) )/eig;

		print "Content-type: text/html${HTTP_NL}${HTTP_NL}".
			"<HTML><H1><CENTER>Unable to Complete Request</H1><BR>\n".
			"<TT>Autopsy</TT> will not follow links from ".
			"untrusted HTML pages:<BR><TT>$url</TT><BR>\n";
	}
	print "${HTTP_NL}${HTTP_NL}";
}


# Alarm Functions
sub sig_alarm_client 
{
	log_session_info("Connection timed out\n");
	close CLIENT;
	exit 1;
}

sub sig_alarm_server 
{
	print "Server Timeout ($STIMEOUT seconds), Exiting\n";
	log_session_info("Server Timeout ($STIMEOUT seconds), Exiting\n");
	exit 0;
}

# Close the system down when Control-C is given
sub sig_close
{
	# delete the cookie file
	if (($USE_COOKIE == 1) && ($SAVE_COOKIE == 1)) {
		unlink "$cook_file";
	}

	print "End Time: ".localtime()."\n";
	log_session_info("Ending session on port $port and $hn\n");
	exit 0;
}

# Pass the remote IP address as the argument for logging
sub spawn_cli 
{
	# Set timeout for 10 seconds if we dont get any input
	alarm ($CTIMEOUT);
	$SIG{ALRM} = \&sig_alarm_client;

	while (<STDIN>) {

		if (/^GET \/+(\S*)\s?HTTP/) {
			my $url = $1;
			my $script;
			my $args;

			if (/\x0d\x0a$/) {
				$HTTP_NL = "\x0d\x0a";
			}
			else {
				$HTTP_NL = "\x0a";
			}

			# Magic Cookie 
			# If we are using cookies, then the url should be:
			# cookie/autopsy?var=val ...
            if ($USE_COOKIE == 1) {

            	if (($url =~ /^(\d+)\/+([\w\.\/]+)(?:\?(.*))?$/) && 
				  ($1 == $magic)) {
					$script = $2;  
					$args = $3;
				}
				else {
                   	my @rip = unpack('C4', shift());
                    log_session_info("ERROR: Incorrect Cookie from:".
                      "$rip[0].$rip[1].$rip[2].$rip[3]\n");
                    forbid();
                    return 1;
                }
            }

			# if we aren't using cookies, then it should be:
			# autopsy?var=val ...
			else {
				if ($url =~ /^\/?([\w\.\/]+)(?:\?(.*))?$/) {
					$script = $1;
					$args = $2;
				}
				else {
					bad_req($url);
					return 1;
				}
			}

			if ($script eq $PROGNAME) {
				$args = "" unless (defined $args);

				# Turn timer off
				alarm (0);

				# Print status
				print "HTTP/1.0 200 OK${HTTP_NL}";
				autopsy_main($args);
			}
			# Display the sanitized picture or reference error
			elsif ($script eq $SANITIZE_TAG) {
				sterile($args);
				return 1;
			}
			# Display a picture or help file
			elsif ( ($script =~ /^(pict\/[\w\.\/]+)/)  ||
			  ($script =~ /^(help\/[\w\.\/]+)/) ) {
				show_file ($1);
			}
			elsif ($script eq 'about') {
				about();
			}
			else {
				bad_req($url);
				log_session_info("Unknown function: $script\n");
				return 1;
			}
			return 0;
		}
	} # end of while (<>)

} # end of spawn_cli


# Print the contents of a local picture or help file
sub show_file
{
	my $file = "$INSTALLDIR/".shift;

	if (-e "$file") {
		print "HTTP/1.0 200 OK${HTTP_NL}";

		open FILE, "<$file" or
		  die "can not open $file";

		if ($file =~ /\.jpg$/i) {
			print "Content-type: image/jpeg${HTTP_NL}${HTTP_NL}";
		} elsif ($file =~ /\.gif$/i) {
			print "Content-type: image/gif${HTTP_NL}${HTTP_NL}";
		} elsif ($file =~ /\.html$/i) {
		  	print "Content-type: text/html${HTTP_NL}${HTTP_NL}";
		} else {
			print "HTTP/1.0 404 Bad Request${HTTP_NL}".
				"Content-type: text/html${HTTP_NL}${HTTP_NL}".  
				"<HTML><H2><CENTER>Unknown Extension</H2>".
		  		"</CENTER></HTML>${HTTP_NL}${HTTP_NL}${HTTP_NL}";
			exit(1);
		}

		while (<FILE>) {
			print "$_";
		}
		close (FILE);

		print "${HTTP_NL}${HTTP_NL}";
	}
	else {
		print "HTTP/1.0 404 Bad Request${HTTP_NL}".
		  "Content-type: text/html${HTTP_NL}${HTTP_NL}".
		  "<HTML><H2><CENTER>File Not Found</H2>".
		  "</CENTER></HTML>${HTTP_NL}${HTTP_NL}${HTTP_NL}";
		exit(1);
	}

	return;
}

sub about 
{

	print "HTTP/1.0 200 OK${HTTP_NL}".
	  "Content-type: text/html${HTTP_NL}${HTTP_NL}";


print <<EOF;

<HTML>
<HEAD><TITLE>About Autopsy</TITLE></HEAD>

<BODY BGCOLOR=#CCCC99>

<CENTER><H2>About Autopsy</H2>
  <BR>
  <IMG SRC=\"pict/logo.jpg\">
  <BR><BR>
  <B>Version</B>: $VER
  <BR>
  <TT><A HREF=$AUT_HOME_PAGE>$AUT_HOME_PAGE</A></TT>
  <BR>
</CENTER>


<H3>Credits</H3>
<UL>
  <LI>Code Development: Brian Carrier
  <LI>Interface: Samir Kapuria
</UL>

<H3>Configuration</H3>
<B>Evidence Locker</B>: <TT>$LOCKDIR</TT><BR>
<B>Sleuth Kit</B>: <TT>$TASKDIR</TT><BR>
<B>strings</B>: <TT>$STRINGS_EXE</TT><BR>
<B>grep</B>: <TT>$GREP_EXE</TT><BR>
<B>NSRL</B>: <TT>$NSRLDB</TT><BR>

</BODY></HTML>

EOF
	return 0;
}


### Check that the required tools are there
sub check_tools
{
	# Sleuth Kit execs
	unless (-x "${TASKDIR}icat") {
		print "ERROR: Sleuth Kit icat executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}istat") {
		print "ERROR: Sleuth Kit istat executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}ifind") {
		print "ERROR: Sleuth Kit ifind executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}ils") {
		print "ERROR: Sleuth Kit ils executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}fls") {
		print "ERROR: Sleuth Kit fls executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}ffind") {
		print "ERROR: Sleuth Kit ffind executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}dcat") {
		print "ERROR: Sleuth Kit dcat executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}dcalc") {
		print "ERROR: Sleuth Kit dcalc executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}dls") {
		print "ERROR: Sleuth Kit dls executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}file") {
		print "ERROR: Sleuth Kit file executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}fsstat") {
		print "ERROR: Sleuth Kit fsstat executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}md5") {
		print "ERROR: Sleuth Kit md5 executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}sorter") {
		print "ERROR: Sleuth Kit sorter executable missing\n";
		exit (1);
	}
	unless (-x "${TASKDIR}hfind") {
		print "ERROR: Sleuth Kit hfind executable missing\n";
		print "  You likely have an old version of The Sleuth Kit or TASK\n";
		exit (1);
	}

	unless (-x "$STRINGS_EXE") {
		print "ERROR: strings executable missing\n";
		exit (1);
	}

	unless (-x "$GREP_EXE") {
		print "ERROR: grep executable missing\n";
		exit (1);
	}
}

# check values that should be defined in the configuration files
# This will show incomplete installations
sub check_vars
{
	unless ( (defined $TASKDIR) && ($TASKDIR ne "") ) {
		print "ERROR: TASKDIR variable not set in configuration file\n";
		print "  This could been caused by an incomplete installation\n";
		exit (1);
	}

	unless (-d "$TASKDIR") {
		print "Invalid Sleuth Kit binary directory: $TASKDIR\n";
		exit (1);
	}

	# Verify The evidence locker directory
	unless ( (defined $LOCKDIR) && ($LOCKDIR ne "") ) {
		print "ERROR: LOCKDIR variable not set in configuration file\n";
		print "  This could been caused by an incomplete installation\n";
		exit (1);
	}

	unless (-d "$LOCKDIR") {
		print "Invalid evidence locker directory: $LOCKDIR\n";
		exit (1);
	}

	# Directory Names
	unless ( (defined $IMGDIR) && ($IMGDIR ne "") ) {
		print "ERROR: IMGDIR variable not set in configuration file\n";
		print "  This could been caused by an incomplete installation\n";
		exit (1);
	}
	unless ( (defined $DATADIR) && ($DATADIR ne "") ) {
		print "ERROR: DATADIR variable not set in configuration file\n";
		print "  This could been caused by an incomplete installation\n";
		exit (1);
	}
	unless ( (defined $LOGDIR) && ($LOGDIR ne "") ) {
		print "ERROR: LOGDIR variable not set in configuration file\n";
		print "  This could been caused by an incomplete installation\n";
		exit (1);
	}
	unless ( (defined $REPDIR) && ($REPDIR ne "") ) {
		print "ERROR: REPDIR variable not set in configuration file\n";
		print "  This could been caused by an incomplete installation\n";
		exit (1);
	}
}
