view sshfp.cgi @ 0:d7fc9c3b4d87

Adding SSHFP CGI and update script
author Dominic Cleal <dominic-cleal@cdo2.com>
date Thu, 19 Nov 2009 11:54:21 +0000
parents
children
line wrap: on
line source

#!/usr/bin/perl
#
# Maintains and displays a list of SSH fingerprints for remote hosts.
#
# Permits remote host to update their SSH fingerprint and then displays the
# list of all recorded fingerprints.
#
# Warning: no limits are made on the number of fingerprints that can be stored,
# could potentially be DDoSed with lots of small files.  This script should be
# locked down in the web server config.
#
# Copyright (c) 2009 CDO2 Limited
# Author: Dominic Cleal <dominic-cleal@cdo2.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

use warnings;
use strict;
use CGI;
use Socket;
use POSIX qw/strftime/;

my $STORE = '/var/local/sshfp/';
my $IP_REGEX = qr/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
my $FP_REGEX = qr/^([\da-f]{2}:?){16}$/;

my $cgi = new CGI;
print $cgi->header(-type    => 'text/html',
                   -pragma  => 'no-cache',
                   -expires => '-365d');

my $action = $cgi->param('action') || 'display';
my $sshfp_rsa = $cgi->param('rsa');
my $sshfp_dsa = $cgi->param('dsa');

if ($action eq 'update')
{
    error('Both RSA or DSA fingerprints required')
        unless (defined $sshfp_rsa && defined $sshfp_dsa);
    
    error('RSA fingerprint invalid') unless ($sshfp_rsa =~ $FP_REGEX);
    error('DSA fingerprint invalid') unless ($sshfp_dsa =~ $FP_REGEX);
    
    my $time = time;
    my $ip = $ENV{'REMOTE_ADDR'} || error('IP unavailable');
    error('IP invalid') unless ($ip =~ $IP_REGEX);
    
    open(FH, "> $STORE/$ip");
    print FH "$time,$sshfp_rsa,$sshfp_dsa";
    close(FH);
}
else
{
    print<<END;
<html>
<head><title>SSH fingerprint records</title></head>
<body>
<table border="1">
<tr><th>Hostname</th><th>IP address</th><th>Last update</th>
<th>RSA fingerprint</th><th>DSA fingerprint</th></tr>
END
    
    opendir(my $dh, $STORE) || error("Can't open store: $!");
    my @files = sort grep { $IP_REGEX && -f "$STORE/$_" } readdir($dh);
    closedir $dh;
    
    foreach my $ip (@files)
    {
        my $packedip = inet_aton($ip);
        my $host = gethostbyaddr($packedip, AF_INET);
        
        open(FH, "< $STORE/$ip");
        my @text = split(/,/, <FH>);
        close(FH);
        
        my $time = strftime("%Y-%m-%d %H:%M:%S", gmtime $text[0]);
        
        print("<tr><td>$host</td><td>$ip</td><td>$time</td>" .
              "<td>$text[1]</td><td>$text[2]</td></tr>\n");
    }
    
    print<<END;
</table></body></html>
END
}

sub error
{
    my $text = shift;
    print "ERROR: $text\n";
    die $text;
}