From 8b1c22b7758511461b359461926e47b093a349d3 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 15 Mar 2000 12:13:01 +1100 Subject: - Created contrib/ subdirectory. Included helpers from Phil Hands' Debian package, README file and chroot patch from Ricardo Cerqueira - Moved gnome-ssh-askpass.c to contrib directory and reomved config option. - Slight cleanup to doc files --- contrib/make-ssh-known-hosts.pl | 737 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 737 insertions(+) create mode 100644 contrib/make-ssh-known-hosts.pl (limited to 'contrib/make-ssh-known-hosts.pl') diff --git a/contrib/make-ssh-known-hosts.pl b/contrib/make-ssh-known-hosts.pl new file mode 100644 index 000000000..49c9f618e --- /dev/null +++ b/contrib/make-ssh-known-hosts.pl @@ -0,0 +1,737 @@ +#!/usr/bin/perl -w +# -*- perl -*- +###################################################################### +# make-ssh-known-hosts.pl -- Make ssh-known-hosts file +# Copyright (c) 1995 Tero Kivinen +# All Rights Reserved. +# +# Make-ssh-known-hosts is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY. No author or distributor accepts +# responsibility to anyone for the consequences of using it or for +# whether it serves any particular purpose or works at all, unless he +# says so in writing. Refer to the GNU General Public License for full +# details. +# +# Everyone is granted permission to copy, modify and redistribute +# make-ssh-known-hosts, but only under the conditions described in +# the GNU General Public License. A copy of this license is supposed to +# have been given to you along with make-ssh-known-hosts so you can +# know your rights and responsibilities. It should be in a file named +# gnu-COPYING-GPL. Among other things, the copyright notice and this notice +# must be preserved on all copies. +###################################################################### +# Program: make-ssh-known-hosts.pl +# $Source: /var/cvs/openssh/contrib/Attic/make-ssh-known-hosts.pl,v $ +# Author : $Author: damien $ +# +# (C) Tero Kivinen 1995 +# +# Creation : 19:52 Jun 27 1995 kivinen +# Last Modification : 00:07 Jul 8 1998 kivinen +# Last check in : $Date: 2000/03/15 01:13:03 $ +# Revision number : $Revision: 1.1 $ +# State : $State: Exp $ +# Version : 1.343 +# Edit time : 242 min +# +# Description : Make ssh-known-host file from dns data. +# +# $Log: make-ssh-known-hosts.pl,v $ +# Revision 1.1 2000/03/15 01:13:03 damien +# - Created contrib/ subdirectory. Included helpers from Phil Hands' +# Debian package, README file and chroot patch from Ricardo Cerqueira +# +# - Moved gnome-ssh-askpass.c to contrib directory and reomved config +# option. +# - Slight cleanup to doc files +# +# Revision 1.6 1998/07/08 00:44:23 kivinen +# Fixed to understand bind 8 nslookup output. +# +# Revision 1.5 1998/04/30 01:53:33 kivinen +# Moved kill before close and added sending SIGINT first and +# then 1 second sleep before sending SIGKILL. +# +# Revision 1.4 1998/04/17 00:39:19 kivinen +# Changed to close ssh program filedescriptor before killing it. +# Removed ^ from the password matching prompt. +# +# Revision 1.3 1997/04/17 04:21:27 kivinen +# Changed to use 3des by default. +# +# Revision 1.2 1997/03/26 07:14:01 kivinen +# Added EWOULDBLOCK. +# +# Revision 1.1.1.1 1996/02/18 21:38:10 ylo +# Imported ssh-1.2.13. +# +# Revision 1.4 1995/10/02 01:23:45 ylo +# Ping packet size fixes from Kivinen. +# +# Revision 1.3 1995/08/29 22:37:39 ylo +# Now uses GlobalKnownHostsFile and UserKnownHostsFile. +# +# Revision 1.2 1995/07/15 13:26:37 ylo +# Changes from kivinen. +# +# Revision 1.1.1.1 1995/07/12 22:41:05 ylo +# Imported ssh-1.0.0. +# +# +# +# If you have any useful modifications or extensions please send them to +# Tero.Kivinen@hut.fi +# +###################################################################### +# initialization + +require 5.000; +use Getopt::Long; +use FileHandle; +use POSIX; +use Socket; +use Fcntl; + +$version = ' $Id: make-ssh-known-hosts.pl,v 1.1 2000/03/15 01:13:03 damien Exp $ '; + +$command_line = "$0 "; +foreach $a (@ARGV) { + $command_line .= $a . " "; +} +STDERR->autoflush(1); + +###################################################################### +# default values for options + +$debug = 5; +$defserver = ''; +$bell='\a'; +$public_key = '/etc/ssh_host_key.pub'; +$private_ssh_known_hosts = "/tmp/ssh_known_hosts$$"; +$timeout = 60; +$ping_timeout = 3; +$passwordtimeout = undef; +$trustdaemon = 1; +$domainnamesplit = 0; +$recursive = 1; + +###################################################################### +# Programs and their options + +$nslookup = "nslookup"; + +$ssh="ssh -a -c 3des -x -o 'ConnectionAttempts 1' -o 'FallBackToRsh no' -o 'GlobalKnownHostsFile /dev/null' -o 'KeepAlive yes' -o 'StrictHostKeyChecking no' -o 'UserKnownHostsFile $private_ssh_known_hosts'"; +$sshdisablepasswordoption="-o 'BatchMode yes' -o 'PasswordAuthentication no'"; + +###################################################################### +# Cleanup and initialization + +unlink($private_ssh_known_hosts); +$sockaddr = 'S n a4 x8'; +($junk, $junk, $sshport) = getservbyname("ssh", "tcp"); +if (!defined($sshport)) { + $sshport = 22; +} +($tcpprotoname, $junk, $tcpproto) = getprotobyname('tcp'); +defined($tcpprotoname) || die "getprotobyname : $!"; + +###################################################################### +# Parse options + +GetOptions("initialdns=s", "server=s", "subdomains=s", + "debug=i", "timeout=i", "passwordtimeout=i", + "trustdaemon!", "domainnamesplit", "silent", + "nslookup=s", "pingtimeout=i", "recursive!", + "keyscan", + "ssh=s") + || die "Getopt : $!"; + +if (defined($opt_initialdns)) { $defserver = $opt_initialdns; } + +if (defined($opt_server)) { $server = $opt_server; } + +if (defined($opt_subdomains)) { @subdomains = split(/,/, $opt_subdomains); } + +if (defined($opt_debug)) { $debug = $opt_debug; } + +if (defined($opt_timeout)) { $timeout = $opt_timeout; } + +if (defined($opt_pingtimeout)) { $ping_timeout = $opt_pingtimeout; } + +if (defined($opt_passwordtimeout)) { + $passwordtimeout = $opt_passwordtimeout; + $sshdisablepasswordoption = ''; +} + +if (defined($opt_trustdaemon)) { $trustdaemon = $opt_trustdaemon; } + +if (defined($opt_recursive)) { $recursive = $opt_recursive; } + +if (defined($opt_domainnamesplit)) { $domainnamesplit = $opt_domainnamesplit; } + +if (defined($opt_silent)) { $bell = ''; } + +if (defined($opt_nslookup)) { $nslookup = $opt_nslookup; } + +if (defined($opt_ssh)) { $ssh = $opt_ssh; } else { + $ssh = "$ssh $sshdisablepasswordoption"; +} + +if ($#ARGV == 0) { + $domain = "\L$ARGV[0]\E"; + $grep_yes = '.*'; + $grep_no = '^$'; +} elsif ($#ARGV == 1) { + $domain = "\L$ARGV[0]\E"; + $grep_yes = $ARGV[1]; + $grep_no = '^$'; +} elsif ($#ARGV == 2) { + $domain = "\L$ARGV[0]\E"; + $grep_yes = $ARGV[1]; + $grep_no = $ARGV[2]; +} else { + print(STDERR "$0 [--initialdns initial_dns_server] [--server dns_server] [--subdomains sub.sub.domain,sub.sub,sub,] [--debug debug_level] [--timeout ssh_exec_timeout_in_secs] [--pingtimeout ping_timeout_in_secs] [--passwordtimeout timeout_for_password_in_secs] [--notrustdaemon] [--norecursive] [--domainnamesplit] [--silent] [--keyscan] [--nslookup path_to_nslookup] [--ssh path_to_ssh] full.domain [ host_info_take_regexp [ host_info_remove_regex ]]\n"); + exit(1); +} + +###################################################################### +# Check that ssh program exists + +if (system("$ssh > /dev/null 2>&1 ") != 256) { + print(STDERR "Error: Could not run ssh program ($ssh): $!\nError: Try giving the path to it with --ssh option\n"); + exit(1); +} + +###################################################################### +# Generate subdomains list + +if (!$domainnamesplit) { + debug(6, "Auto splitting host entries"); +} elsif (!defined(@subdomains)) { + debug(6, "Generating subdomain list"); + + # split domain to pieces + @domain_pieces = split(/\./, $domain); + + # add empty domain part + push(@subdomains, ''); + + # add rest parts, except the one before full domain name + $entry=''; + for(; $#domain_pieces > 1; ) { + $entry .= "." . shift(@domain_pieces); + push(@subdomains, $entry); + } + + # add full domain name + push(@subdomains, ".$domain"); + debug(5, "Subdomain list: " . join(',', @subdomains)); +} else { + debug(5, "Using given subdomain list:" . join(',', @subdomains)); +} + +###################################################################### +# finding SOA entry for domain + +@other_servers = (); +if (!defined($server)) { + debug(6, "Finding DNS database SOA entry"); + + ($server, @other_servers) = find_soa($domain, $defserver); + + if (!defined($server)) { + print(STDERR "Error: Could not find DNS SOA entry from default dns server\nError: Try giving the initial nameserver with --initialdns option\n"); + exit(1); + } else { + debug(5, "DNS server found : $server"); + } +} else { + debug(5, "Using given DNS server : $server"); +} + +###################################################################### +# Print header + +($name, $junk, $junk, $junk, $junk, $junk, $gecos) = getpwuid($<); +$gecos =~ s/,.*$//g; + +if (!defined($opt_keyscan)) { + print(STDOUT "# This file is generated with make-ssh-known-hosts.pl\n"); + print(STDOUT "#$version\n"); + print(STDOUT "# with command line :\n"); + print(STDOUT "# $command_line\n"); + print(STDOUT "#\n"); + print(STDOUT "# The script was run by $gecos ($name) at " . localtime() . "\n"); + print(STDOUT "# using perl ($^X) version $].\n"); +} + +###################################################################### +# Get DNS database list from server + +do { + $domains_done{$domain} = 1; + delete $domains_waiting{$domain}; + + $hostcnt = 0; + $cnamecnt = 0; + $lines = 0; + $soa = 0; + undef %host; + undef %cname; + undef %hostdata; + + dnsagain: + debug(1, "Getting DNS database for $domain from server $server"); + open(DNS, "echo ls -d $domain | nslookup - $server 2>&1 |") || + die "Error: Could not start nslookup to make dns list : $!\nError: Try giving --nslookup option and telling the path to nslookup program\n"; + + while() { + $lines++; + chomp; + undef $hostname if/^\s*$/; + if (/^\s{0,1}([a-zA-Z0-9-]\S*)/) { + $hostname = "\L$1\E"; + } + next unless defined $hostname; + if (/^.*\s(SOA)\s+(.*)\s*$/ || $hostname eq "SOA") { + undef $soa if(/^.*\s(SOA)\s+(.*)\s*$/); + $data = $_ if ($hostname eq "SOA"); + $data = $2 unless $hostname eq "SOA"; + $data =~ s/\s*;.*$//; + $data =~ s/^\s+//; + if( defined $soa ) { + $soa .= " \L$data\E"; + } else { + $soa = "\L$data\E"; + } + $hostname = "SOA"; + } elsif (/^.*\s(A|CNAME|NS)\s+(.*)\s*$/) { + $host = $hostname; + $field = "\L$1\E"; + $data = "\L$2\E"; + debug(70, "Line = /$host/$field/$data/"); + if ($host !~ /\.$/) { + $host .= ".$domain"; + } else { + $host =~ s/\.$//g; + } + if ($field eq "a") { + if ($host =~ /$domain$/) { + if (defined($host{$host})) { + $host{$host} .= ",$data"; + } else { + $host{$host} = "$data"; + $hostcnt++; + } + debug(30, "$host A == $host{$host}"); + } + } elsif ($field eq "cname") { + if ($data !~ /\.$/ && ! /^\s/ ) { + $data .= ".$domain"; + } else { + $data =~ s/\.$//g; + } + if ($host =~ /$domain$/) { + if (defined($cname{$data})) { + $cname{$data} .= ",$host"; + } else { + $cname{$data} = "$host"; + $cnamecnt++; + } + debug(30, "$host CNAME $data"); + $junk = $data; + $data = $host; + $host = $junk; + } + } elsif ($field eq "ns") { + if (!defined($domains_done{$host})) { + if (!defined($domains_waiting{$host})) { + debug(10, "Adding subdomain $host to domains list, with NS $data"); + $domains_waiting{$host} = $data; + push(@domains_waiting, $host); + } else { + debug(10, "Adding NS $data for domain $host"); + $domains_waiting{$host} .= ",$data"; + } + } + } + if (!defined($hostdata{$host})) { + $hostdata{$host} = "$host\n$field=$data\n"; + } else { + $hostdata{$host} .= "$field=$data\n"; + } + } + } + close(DNS); + if ($hostcnt == 0 && $cnamecnt == 0) { + if ($#other_servers != -1) { + $server = shift(@other_servers); + goto dnsagain; + } + } + debug(1, "Found $hostcnt hosts, $cnamecnt CNAMEs (total $lines lines)"); + if (!defined($opt_keyscan)) { + print(STDOUT "#\n"); + print(STDOUT "# Domain = $domain, server = $server\n"); + print(STDOUT "# Found $hostcnt hosts, $cnamecnt CNAMEs (total $lines lines)\n"); + print(STDOUT "# SOA = $soa\n"); + print(STDOUT "#\n"); + } + +###################################################################### +# Loop through hosts and try to connect to hosts + + foreach $i (sort (keys %host)) { + debug(50, "Host = $i, Hostdata = $hostdata{$i}"); + if ($hostdata{$i} =~ /$grep_yes/im && + $hostdata{$i} !~ /$grep_no/im && + $i !~ /^localhost\./ && + $host{$i} !~ /^127.0.0.1$|^127.0.0.1,|,127.0.0.1$|,127.0.0.1,/) { + debug(2, "Trying host $i"); + + @hostnames = (); + if (defined($cname{$i})) { + expand($i, \@hostnames, \@subdomains); + foreach $j (split(/,/, $cname{$i})) { + expand($j, \@hostnames, \@subdomains); + } + } else { + expand($i, \@hostnames, \@subdomains); + } + foreach $j (split(/,/, $host{$i})) { + push(@hostnames, $j); + } + $hostnames = join(',', (@hostnames)); + + if (defined($opt_keyscan)) { + printf(STDOUT "$host{$i}\t$hostnames\n"); + } elsif (try_ping($i, $host{$i})) { + $trusted = 1; + $err = 'Timeout expired'; + $ssh_key = try_ssh("$i"); + if (!defined($ssh_key)) { + $ssh_key = find_host_from_known_hosts($i); + $trusted = 0; + } + if (defined($ssh_key)) { + if ($trusted) { + debug(2, "Ssh to $i succeded"); + } else { + debug(2, "Ssh to $i failed, using local known_hosts entry"); + } + debug(4, "adding entries : $hostnames"); + $ssh_key =~ s/root@//i; + if (!$trusted && !$trustdaemon) { + print(STDOUT "# $hostnames $ssh_key\n"); + } else { + print(STDOUT "$hostnames $ssh_key\n"); + } + } else { + debug(2, "ssh failed : $err"); + } + } else { + debug(2, "ping failed"); + } + } else { + debug(10, "Skipped host $i"); + } + } + again: + $domain = shift(@domains_waiting); + if (defined($domain)) { + $server = $domains_waiting{$domain}; + @other_servers = split(',', $server); + $server = shift(@other_servers); + ($server, @other_servers) = find_soa($domain, $server); + if(!defined($server)) { + debug(1, "Skipping domain $domain because no DNS SOA entry found"); + $domains_done{$domain} = 1; + delete $domains_waiting{$domain}; + goto again; + } + } +} while ($recursive && defined($domain)); + +unlink($private_ssh_known_hosts); +exit (0); + +###################################################################### +# try_ping -- try to ping to host and return 1 if success +# $success = try_ping($host, $list_ip_addrs); + +sub try_ping { + my($host, $ipaddrs) = @_; + my(@ipaddrs, $ipaddr, $serv, $ip); + my($rin, $rout, $win, $wout, $nfound, $tmout, $buf, $len, $ret, $err); + + $buf = ''; + debug(51,"Trying to ping host $host"); + @ipaddrs = split(/,/, $ipaddrs); + + while ($ipaddr = shift(@ipaddrs)) { + + debug(55,"Trying ipaddr $ipaddr"); + + #initialize socket + socket(PING, PF_INET, SOCK_STREAM, $tcpproto) || + die "socket failed : $!"; + setsockopt(PING, SOL_SOCKET, SO_REUSEADDR, 1) || + die "setsockopt failed : $!"; + PING->autoflush(1); + fcntl(PING, F_SETFL, fcntl(PING, F_GETFL, 0) | POSIX::O_NONBLOCK) || + die "fcntl failed : $!"; + + $ip = pack('C4', split(/\./, $ipaddr, 4)); + $serv = pack($sockaddr, AF_INET, $sshport, $ip); + + again: + # try connect + $ret = connect(PING, $serv); + $err = $!; + if (!$ret) { + debug(60, "Connect failed : $err"); + if ($err == EINTR) { + goto again; + } + # socket not yet connected, wait for result, it will + # wake up for writing when done + $tmout = $ping_timeout; + + $rin = ''; + $win = ''; + vec($rin, fileno(PING), 1) = 1; + vec($win, fileno(PING), 1) = 1; + debug(60, "Waiting in select, rin = " . unpack('H*', $rin) . + ", win = " . unpack('H*', $win)); + ($nfound) = select($rout = $rin, $wout = $win, undef, $tmout); + $err = $!; + debug(80, "Select returned $nfound, rout = " . unpack('H*', $rout) . + ", wout = " . unpack('H*', $wout)); + if ($nfound != 0) { + # connect done, read the status with sysread + $ret = sysread(PING, $buf, 1); + $err = $!; + if (defined($ret) || $err == EAGAIN || $err == EWOULDBLOCK) { + debug(60, "Select ok, read ok ($err), returning ok"); + # connection done, return ok + shutdown(PING, 2); + close(PING); + return 1; + } else { + # connection failed, try next ipaddr + debug(60, "Select ok, read failed : $err, trying next"); + close(PING); + } + } else { + # timeout exceeded, try next ipaddr + debug(60, "Select failed : $err, trying next"); + close(PING); + } + } else { + # connect succeeded, return ok. + debug(60, "Connect ok, returning ok"); + shutdown(PING, 2); + close(PING); + return 1; + } + } + debug(60, "Returning fail"); + return 0; +} + +###################################################################### +# try_ssh -- try ssh connection to host and return ssh_key if success +# if failure return undef, and set $err string to contain error message. +# $ssh_key = try_ssh($host); + +sub try_ssh { + my($host) = @_; + my($buf, $ret, $pos, $pid, $rin, $nfound, $tmout); + + $pid = open(SSH, "$ssh $host cat $public_key 2>&1 |"); + $err = undef; + + if ($pid == 0) { + $err = "could not open ssh connection to host"; + return undef; + } + $ret = 1; + $pos = 0; + $buf = ''; + $tmout = $timeout; + debug(10, "Starting ssh select loop"); + loop: + while (1) { + + $rin = ''; + vec($rin, fileno(SSH), 1) = 1; + ($nfound, $tmout) = select($rin, undef, undef, $tmout); + + # Timeout + if ($nfound <= 0) { + debug(20, "Ssh select timed out"); + kill(2, $pid); sleep(1); kill(9, $pid); + close(SSH); + $err = "Timeout expired"; + return undef; + } + + $ret = sysread(SSH, $buf, 256, $pos); + # EOF or error + if ($ret <= 0) { + # Yes, close the pipe and return + close(SSH); + debug(20, "Ssh select closed status = $?"); + $err = "No reply from ssh"; + return undef; + } + $pos += $ret; + while ($buf =~ /^(.*)\n\r?([\000-\377]*)$/) { + $_ = $1; + $buf = $2; + $pos = length($buf); + debug(20, "Ssh select loop, line = \"$_\""); + if (/^connection.*refused/i) { + $err = "connection refused"; + } elsif (/^permission/i) { + $err = "permission denied"; + } elsif (/$public_key.*no\s+file/i) { + $err = "$public_key file not found"; + } elsif (/$public_key.*permission\s+denied/i) { + $err = "$public_key file permission denied"; + } elsif (/^\d+\s+\d+\s+\d/) { + kill(2, $pid); sleep(1); kill(9, $pid); + close(SSH); + return $_; + } + if (defined($err)) { + kill(2, $pid); sleep(1); kill(9, $pid); + close(SSH); + return undef; + } + } + if ($buf =~ /password: $/i) { + if (defined($passwordtimeout)) { + $tmout = $passwordtimeout; + print(STDERR "$bell\n\rPassword: "); + if ($tmout == 0) { + $tmout = undef; + } + } else { + $tmout = 0; + } + $buf = ''; + $pos = 0; + } + } +} + +###################################################################### +# find_hosts_from_known_hosts -- find host key from private known_hosts file +# $ssh_key = find_host_from_known_hosts($host); + +sub find_host_from_known_hosts { + my($host) = @_; + open(KNOWNHOSTS, "<$private_ssh_known_hosts") || return undef; + while() { + @_ = split(/\s+/, $_); + if ($_[0] =~ /^$host$|^$host,|,$host$/) { + shift(@_); + close(KNOWNHOSTS); + return join(' ', @_); + } + } + close(KNOWNHOSTS); + return undef; +} + +###################################################################### +# expand -- insert expanded hostnames to hostnames table +# expand($hostname, \@hostnames, \@subdomains); + +sub expand { + my($host, $hostnames, $subdomains) = @_; + my($newhost, $sub, $entry); + + if (!$domainnamesplit) { + my(@domain_pieces); + + # split domain to pieces + @domain_pieces = split(/\./, $host); + + # add rest parts, except the one before full domain name + $entry = shift(@domain_pieces); + + debug(20, "Adding autosplit entry $entry"); + push(@$hostnames, $entry); + + for(; $#domain_pieces > 1; ) { + $entry .= "." . shift(@domain_pieces); + debug(20, "Adding autosplit entry $entry"); + push(@$hostnames, $entry); + } + # add full domain name + debug(20, "Adding autosplit entry $host"); + push(@$hostnames, $host); + } else { + if ($host =~ /^(.*)$domain$/i) { + $newhost = $1; + $newhost =~ s/\.$//g; + foreach $sub (@$subdomains) { + $entry = $newhost . $sub; + $entry =~ s/^\.//g; + if ($entry ne '') { + debug(20, "Adding entry $entry"); + push(@$hostnames, $entry); + } + } + } + } +} + +###################################################################### +# Print debug text +# debug(text_debug_level, string) + +sub debug { + my($level, $str) = @_; + if ($debug > $level) { + print(STDERR "$0:debug[$level]: $str\n"); + } +} + +###################################################################### +# find_soa -- find soa entry for domain +# ($soa_origin, @other_servers) = find_soa($domain, $initial_server) + +sub find_soa { + my($domain, $initial_server) = @_; + my($field, $data, $server, @other_servers); + + open(DNS, "$nslookup -type=soa $domain $initial_server 2>&1 |") || + die "Error: Could not start nslookup to find SOA entry for $domain : $!\nError: Try giving the path to it with --nslookup option\n"; + + while () { + if (/^[^=]*origin\s*=\s*(.*)/) { + $server = $1; + debug(10, "Found origin : $1"); + } elsif (/^[^=]*nameserver\s*=\s*(.*)\s*$/) { + push(@other_servers, $1); + debug(10, "Found nameserver : $1"); + } + } + close(DNS); + return($server, @other_servers); +} + +###################################################################### +# make_perl_happy -- use some symbols, so perl doesn't complain so much +# make_perl_happy(); + +sub make_perl_happy { + if (0) { + print $opt_silent; + } +} + +1; -- cgit v1.2.3