Article 8077 of comp.lang.perl: Xref: feenix.metronet.com alt.sources:2275 comp.lang.perl:8077 Path: feenix.metronet.com!news.ecn.bgu.edu!usenet.ins.cwru.edu!howland.reston.ans.net!europa.eng.gtefsd.com!uunet!munnari.oz.au!goanna.cs.rmit.oz.au!yallara!lm From: lm@yallara.cs.rmit.OZ.AU (Luke Mewburn) Newsgroups: alt.sources,comp.lang.perl Subject: checking rhost files for dodgy entries Followup-To: comp.lang.perl Date: 17 Nov 1993 23:43:25 GMT Organization: Technical Support Group, Dept. of Computer Science, RMIT Lines: 240 Message-ID: <2cecut$lal@goanna.cs.rmit.oz.au> Reply-To: zak@rmit.edu.au NNTP-Posting-Host: yallara.cs.rmit.oz.au Keywords: perl, rhosts, security Here's a script I whipped up to check the rhost entries of the users on your machine for `dodgyness'. Looks for `+', `*' (umax v equiv of `+'), differing usernames, unknown machine names, etc. Won't check uid's < 100 or > 59998 (configurable), so you don't get complaints about 'daemon' != 'root' :) It has a `rewrite' option to `fix' the suspect lines, and has two different output modes. Will check either every user on the system (with the restrictions above), or only the users specified on the command line. Got some surprising results the first few times I ran it. PS: if you don't have sys/socket.ph, just comment out the require - it makes a pretty reasonable guess about what AF_INET should be anyway :) Luke. --- cut here --- file: ~/perl/chk_rhost #!/usr/bin/perl # # chk_rhost - # checks the .rhosts files for every user for invalid entries # # This program is placed in the public domain, and may be freely used # with the restriction that this header remains intact, and you do not # try to claim you wrote this. # # Author: Luke Mewburn # Date: 931107 # # 931109, lm: do (reverse & forward) name lookups on hosts, check for + # 931110, lm: cache even machines that failed. (e.g, goanna.oz) # 931115, lm: - use correct value for AF_INET from sys/socket.ph, and # make (min|max)uid constants instead of hardcoded values # - added -c & usage # 931116, lm: by default, it checks. Needs -r for rewrite. Added # -v (verbose) & -l (line mode). Added cmdline support. # check for `*' (UMAX V equiv of +) # # TODO: add netgroup support # require 'getopts.pl'; require 'sys/socket.ph'; # get defn for AF_INET $AF_INET = defined(&AF_INET) ? &AF_INET : 2; $| = 1; # unbuffer STDOUT $min_uid = 100; # don't check uids < $minuid $max_uid = 59998; # don't check uids > $maxuid setpwent; # rewind to start of passwd db $progname = $0; # get basename of what we're executed as $progname =~ s/.*\/([^\/]+)/$1/; &Getopts('rvl') || &usage; $do_rewrite = $opt_r; $verbose = $opt_v; $linemode = $opt_l; $frmargv = 1 if (@ARGV); MAIN: # get a user while (@user=($frmargv ? getpwnam(shift(@ARGV)) : getpwent)) { ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell) = @user; next MAIN if ($uid < $min_uid || $uid > $max_uid); # skip nobody && system users $rhfile = $dir . "/.rhosts"; next MAIN if (! -e $rhfile || -z _); open(RH, $rhfile) || next MAIN; # skip what I can't open select(STDOUT); print "Processing $name ($gcos), $rhfile\n" if ($verbose); $had_error = 0; if ($do_rewrite) { rename($rhfile, $rhfile . ".BAD"); # backup old file open(RHOUT, ">$rhfile"); # open new file, set perms chown($uid, $gid, $rhfile); chmod(0600, $rhfile); select(STDOUT); } RHLINE: while ($line = ) { # for each line local($minushost, $minususer); chop ($line); if ($line eq "") { # empty lines &mesg("null entry"); next RHLINE; } if ($line =~ /\+/o) { # lines with a `+' &mesg("+ entry"); next RHLINE; } if ($line =~ /\*/o) { # lines with a `*' &mesg("* entry"); next RHLINE; } ($host, $user, $rest) = split(/[\t ]+/o, $line); $host =~ tr/[A-Z]/[a-z]/; if ($rest) { # lines with > 2 lines &mesg("entry has too many fields"); next RHLINE; } if ($host =~ /^-(.*)/o) { # host prefixed with `-' $minushost = '-'; $host = $1; #print "-host $host\n"; } $user = $name if (! $user); # if no user, assume it's us. if ($user =~ /^-(.*)/o) { # username prefixed with `-' $minususer = '-'; $user = $1; #print "- user $user\n"; } if ($user ne $name) { # different usernames &mesg("user '$user' != '$name'"); next RHLINE; } # validate host if ($host =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/o) { # have IP # only.. $ipaddr = pack('C4', $1, $2, $3, $4); } else { # have name, resolv to IP # $ipaddr = &gethostaddr($host); if (! $ipaddr) { &mesg("IP name '$host' couldn't be resolved"); next RHLINE; } } $resolvhost = &gethostname($ipaddr); # convert hostip -> name if (! $resolvhost) { &mesg("IP " . &addr2asc($ipaddr) . " couldn't be resolved"); next RHLINE; } # if ($host !~ /$resolvhost/i) { # &mesg("hostnames not equivalent"); # next RHLINE; # } # write new entry print RHOUT "$minushost$resolvhost\t$minususer$user\n" if ($do_rewrite); } # while () print "\n" if ($had_error && $verbose); close(RH); close(RHOUT) if ($do_rewrite); } # while endpwent; exit 0; # Justin Case # # mesg - # just dump an error message about the appropriate line # sub mesg { local($mesg) = @_; if ($linemode) { print "$name, $.: $line\n"; } else { print "ERROR: line $., $rhfile\t$mesg\n"; } $had_error = 1; # flag message to print \n } # mesg # # gethostname - # check the addrcache and return the value if we have one, # otherwise actually attempt to resolv. # Args: addr # Returns: name, or 0 if failed # sub gethostname { local($addr) = @_; return $addrcache{$addr} if (defined($addrcache{$addr})); print &addr2asc($addr), " not cached... resolving\n" if ($verbose); local($hname, $halias, $haddrtype, $hlength, @haddrs) = gethostbyaddr($addr, $AF_INET); $hname = 0 if (!defined($hname)); return ($addrcache{$addr} = $hname); } # gethostname # # gethostaddr - # check the namecache and return the value if we have one, # otherwise actually attempt to resolv. # Args: $name # Returns: addr, or 0 if failed # sub gethostaddr { local($name) = @_; return $namecache{$name} if (defined($namecache{$name})); print "$name not cached... resolving\n" if ($verbose); local($hname, $halias, $haddrtype, $hlength, @haddrs) = gethostbyname($name); $haddrs[0] = 0 if (!defined($haddrs[0])); return ($namecache{$name} = $haddrs[0]); } # gethostaddr # # addr2asc - # converts a 4 byte number to the dotted decimal notation string. # sub addr2asc { local($addr) = @_; local($a, $b, $c, $d) = unpack('C4', $addr); return join(".", $a, $b, $c, $d); } # addr2asc # # usage- # print a usage # sub usage { print < Department of Computer Science, RMIT .