1 2######################################################## 3# Please file all bug reports, patches, and feature 4# requests under: 5# https://sourceforge.net/p/logwatch/_list/tickets 6# Help requests and discusion can be filed under: 7# https://sourceforge.net/p/logwatch/discussion/ 8######################################################## 9 10####################################################### 11## Copyright (c) 2008 Kirk Bauer 12## Covered under the included MIT/X-Consortium License: 13## http://www.opensource.org/licenses/mit-license.php 14## All modifications and contributions by other persons to 15## this script are assumed to have been donated to the 16## Logwatch project and thus assume the above copyright 17## and licensing terms. If you want to make contributions 18## under your own copyright or a different license this 19## must be explicitly stated in the contribution an the 20## Logwatch project reserves the right to not accept such 21## contributions. If you have made significant 22## contributions to this script and want to claim 23## copyright please contact logwatch-devel@lists.sourceforge.net. 24######################################################### 25 26use strict; 27use Logwatch ':all'; 28 29my $Debug = $ENV{'LOGWATCH_DEBUG'} || 0; 30my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; 31my $IgnoreHost = $ENV{'sshd_ignore_host'} || ""; 32my $RefusedConnectionsThreshold = $ENV{'refused_connections_threshold'} || 0; 33my $DebugCounter = 0; 34 35# No sense in running if 'sshd' doesn't even exist on this system... 36#unless (( -f "/usr/sbin/sshd" ) or ( -f "/usr/local/sbin/sshd") or ( -f "/usr/lib/ssh/sshd")) { 37# exit (0); 38#} 39 40my %Users = (); 41my %IllegalUsers = (); 42my %PotentialIllegalUsers = (); 43my %TooManyFailures = (); 44my %NoIdent = (); 45my %BindFailed = (); 46my %BadLogins = (); 47my %NoRevMap = (); 48my %RefusedConnections = (); 49my %RefusedAuthentication = (); 50my %NegotiationFailed = (); 51my %DisconnectReceived = (); 52my %RootLogin = (); 53my %PamReleaseFail = (); 54my %PamError = (); 55my %PamChroot = (); 56my %PamDeny = (); 57my %ShadowInfo = (); 58my %TTYModesFail = (); 59my %LoginLock = (); 60my %PostPonedAuth = (); 61my %LockedAccount = (); 62my %AllowUsers = (); 63my %DenyUsers = (); 64my %AllowGroups = (); 65my %DenyGroups = (); 66my %NoGroups = (); 67my %NoShellUsers = (); 68my %ShellNotExecutableUsers = (); 69my %DeprecatedOption = (); 70my %MisMatch = (); 71my %KrbAutFail = (); 72my %KrbAutErr = (); 73my %KrbErr = (); 74my @Scanned = (); 75my %OtherList = (); 76my %ChmodErr = (); 77my %ChownErr = (); 78my %Krb_realm = (); 79my %ConnectFailed = (); 80my %Chroot = (); 81my %CloseDir = (); 82my %CloseFileReadWrite = (); 83my %OpenDir = (); 84my %OpenFile = (); 85my %RealPath = (); 86my %Session = (); 87my %SetModtime = (); 88my %Stat = (); 89my %ClientVers = (); 90 91my $sftpRequests = 0; 92my $NetworkErrors = 0; 93my $Kills = 0; 94my $Starts = 0; 95my $NetworkErrors = 0; 96my $StatusNoSuchFile = 0; 97my $BytesSent = 0; 98my $BytesReceived = 0; 99 100if ( $Debug >= 5 ) { 101 print STDERR "\n\nDEBUG: Inside SSHD Filter \n\n"; 102 $DebugCounter = 1; 103} 104 105while (defined(my $ThisLine = <STDIN>)) { 106 if ( $Debug >= 5 ) { 107 print STDERR "DEBUG($DebugCounter): $ThisLine"; 108 $DebugCounter++; 109 } 110 chomp($ThisLine); 111 if ( 112 ($ThisLine =~ /^pam_succeed_if: requirement "uid < 100" (not|was) met by user /) or 113 ($ThisLine =~ m/^(log: )?$/ ) or 114 ($ThisLine =~ m/^(log: )?\^\[\[60G/ ) or 115 ($ThisLine =~ m/^(log: )? succeeded$/ ) or 116 ($ThisLine =~ m/^(log: )?Closing connection to/) or 117 ($ThisLine =~ m/^(log: )?Starting sshd:/ ) or 118 ($ThisLine =~ m/^(log: )?sshd \-TERM succeeded/ ) or 119 ($ThisLine =~ m/^Bad protocol version identification .*:? [\d.]+/ ) or 120 ($ThisLine =~ m/^Bad protocol version identification.*Big-Brother-Monitor/ ) or 121 ($ThisLine =~ m/^Connection closed by/) or 122 ($ThisLine =~ m/^Disconnecting: Command terminated on signal \d+/) or 123 ($ThisLine =~ m/^Disconnecting: server_input_channel_req: unknown channel -?\d+/) or 124 ($ThisLine =~ m/^connect from \d+\.\d+\.\d+\.\d+/) or 125 ($ThisLine =~ m/^fatal: Timeout before authentication/ ) or 126 ($ThisLine =~ m/^fatal: no hostkey alg/) or 127 ($ThisLine =~ m/Connection from .* port /) or 128 ($ThisLine =~ m/Postponed (keyboard-interactive|publickey) for [^ ]+ from [^ ]+/) or 129 ($ThisLine =~ m/Read from socket failed/) or 130 ($ThisLine =~ m/sshd startup\s+succeeded/) or 131 ($ThisLine =~ m/sshd shutdown\s+succeeded/) or 132 ($ThisLine =~ m/^Found matching [DR]SA key: /) or 133 ($ThisLine =~ m/^error: key_read: type mismatch: encoding error/) or 134 ($ThisLine =~ m/^channel_lookup: -?\d+: bad id/) or 135 ($ThisLine =~ m/^error: channel \d+: chan_read_failed for istate/) or 136 # Result of setting PermitRootLogin to forced-commands-only 137 ($ThisLine =~ m/^Root login accepted for forced command\.( \[preauth\])?$/) or 138 # usually followed by a session opened for user 139 ($ThisLine =~ m/^pam_krb5\[\d+\]: authentication succeeds for /) or 140 ($ThisLine =~ m/^nss_ldap: reconnect/) or 141 ($ThisLine =~ m/^pam_ldap: error trying to bind as user "[^"]+" \(Invalid credentials\)/) or 142 ($ThisLine =~ m/^pam_ldap: ldap_starttls_s: Can't contact LDAP server/) or 143 ($ThisLine =~ m/^pam_sss\(sshd:.*\)/) or 144 ($ThisLine =~ m/^\(pam_unix\) .*/) or 145 ($ThisLine =~ m/^pam_unix\(.*:.*\)/) or 146 ($ThisLine =~ m/^pam_unix_auth:/) or 147 ($ThisLine =~ m/^pam_sepermit\(.*:.*\)/) or 148 ($ThisLine =~ /pam_krb5: authentication succeeds for `([^ ]*)'/) or 149 ($ThisLine =~ /pam_succeed_if\(.*:.*\): error retrieving information about user [a-zA-Z]*/ ) or 150 ($ThisLine =~ /pam_winbind\(sshd:account\): user .* granted access/) or 151 ($ThisLine =~ /pam_winbind\(sshd:account\): user .* OK/) or 152 ($ThisLine =~ /pam_systemd\(sshd:session\): Moving/) or 153 ($ThisLine =~ /pam_systemd\(sshd:session\): .*: Connection reset by peer/) or 154 ($ThisLine =~ /PAM \d+ more authentication failures?;/) or 155 ($ThisLine =~ /^PAM service\(sshd\) ignoring max retries;/) or 156 ($ThisLine =~ /^Failed keyboard-interactive for <invalid username> from/ ) or 157 ($ThisLine =~ /^Keyboard-interactive \(PAM\) userauth failed/ ) or 158 ($ThisLine =~ /^debug1: /) or 159 ($ThisLine =~ /Set \/proc\/self\/oom_(score_)?adj (from -?\d )?to -?\d/ ) or 160 ($ThisLine =~ /Starting session: (forced-command|subsystem|shell|command)/ ) or 161 ($ThisLine =~ /Found matching \w+ key:/ ) or 162 ($ThisLine =~ /User child is on pid \d/ ) or 163 ($ThisLine =~ /Nasty PTR record .* is set up for [\da-fA-F.:]+, ignoring/) or 164 ($ThisLine =~ /Exiting on signal / ) or 165 ($ThisLine =~ /Disconnected from [\da-fA-F.:]* port \d*/ ) or 166 ($ThisLine =~ /Disconnected from user \S+ [\da-fA-F.:]* port \d*/ ) or 167 ($ThisLine =~ /Disconnected from (authenticating|invalid) user \S+ [\da-fA-F.:]* port \d*/ ) or 168 ($ThisLine =~ /Disconnecting( (authenticating|invalid) user .* port \d+)?: Too many authentication failures \[preauth\]/ ) or 169 ($ThisLine =~ /Disconnecting( (authenticating|invalid) user .* port \d+)?: Change of username or service not allowed: .* \[preauth\]/ ) or 170 ($ThisLine =~ /Failed to release session: Interrupted system call/) or 171 ($ThisLine =~ /Close session: user /) or 172 0 # This line prevents blame shifting as lines are added above 173 ) { 174 # Ignore these 175 } elsif ( my ($Method,$User,$Host,$Port,$Key,$FingerP) = ($ThisLine =~ /^Accepted (\S+) for ((?:invalid user )?\S+) from ([\d\.:a-f]+)(?:%\w+)? port (\d+) ssh[12](?:: (\w+) (.+))?/) ) { 176 if ($Debug >= 5) { 177 print STDERR "DEBUG: Found -$User logged in from $Host using $Method ($Key)\n"; 178 } 179 if ($Detail >= 20) { 180 $Users{$User}{$Host}{$Method . ($Key ? 181 "($Key" . (($Detail >= 30) ? " " . $FingerP : "") . ")" : 182 "")}++; 183 } else { 184 if ( $Host !~ /$IgnoreHost/ ) { 185 $Users{$User}{$Host}{"(all)"}++; 186 } 187 } 188 } elsif ( my ($Method, undef,$User,$Host,$Port) = ($ThisLine =~ m/^Failed (\S+) for (illegal|invalid) user (.*) from ([^ ]+) port (\d+)/ ) ) { #openssh 189 $IllegalUsers{$Host}{$User}++; 190 } elsif ( my ($User) = ( $ThisLine =~ /Disconnecting: Too many authentication failures for ([^ ]+)/)) { 191 $TooManyFailures{$User}++; 192 } elsif ( my ($User) = ( $ThisLine =~ /error: maximum authentication attempts exceeded for ([^ ]+) from [^ ]+ port \d+ ssh2 \[preauth\]/)) { 193 $TooManyFailures{$User}++; 194 } elsif ( my ($User,$Host) = ( $ThisLine =~ /error: maximum authentication attempts exceeded for invalid user ([^ ]+) from ([^ ]+) port \d+ ssh2 \[preauth\]/)) { 195 $IllegalUsers{$Host}{$User}++; 196 } elsif ( $ThisLine =~ m/^(fatal: )?Did not receive ident(ification)? string from (\S+)/ ) { # ssh/openssh 197 my $name = LookupIP($3); 198 $NoIdent{$name}++; 199 } elsif ( my ($Host) = ($ThisLine =~ /Could not write ident string to ([^ ]+)$/ )) { 200 my $name = LookupIP($Host); 201 $NoIdent{$name}++; 202 } elsif ( 203 ($ThisLine =~ m/^fatal: Connection closed by remote host\./ ) or 204 ($ThisLine =~ m/^(|fatal: )Read error from remote host(| [^ ]+): Connection reset by peer/ ) or 205 ($ThisLine =~ m/^Read error from remote host [^ ]+: (Connection timed out|No route to host)/ ) or 206 ($ThisLine =~ m/^fatal: Read from socket failed: No route to host/) or 207 ($ThisLine =~ m/^fatal: Write failed: Network is unreachable/ ) or 208 ($ThisLine =~ m/^fatal: Write failed: Broken pipe/) or 209 ($ThisLine =~ m/^fatal: Write failed: Connection reset by peer/) or 210 ($ThisLine =~ m/^Connection reset by/) or 211 ($ThisLine =~ m/^channel \d+: open failed: (?:connect failed: Channel open failed\.|administratively prohibited: open failed)/) or 212 ($ThisLine =~ m/^session_input_channel_req: no session \d+ req window-change/) or 213 ($ThisLine =~ m/^error: chan_shutdown_read failed for .+/) 214 ) { 215 $NetworkErrors++; 216 } elsif ( $ThisLine =~ m/^(log: )?Received (signal 15|SIG...); (terminating|restarting)\./) { #ssh/openssh 217 $Kills++; 218 if ( $Debug >= 5 ) { 219 print STDERR "DEBUG: Found -Signal 15 Terminating- line\n"; 220 } 221 } elsif ( $ThisLine =~ m/^(log: )?Server listening on( [^ ]+)? port \d+/ ) { #ssh/openssh 222 $Starts++; 223 if ( $Debug >= 5 ) { 224 print STDERR "DEBUG: Found -Listening on port 22- line\n"; 225 } 226 } elsif ( my ($Port,$Address,$Reason) = ($ThisLine =~ /^error: Bind to port ([^ ]+) on ([^ ]+) failed: (.+).$/ )) { 227 my $Temp = "$Address port $Port ($Reason)"; 228 # Failed to bind on 0.0.0.0 likely due to configured "ListenAddress" 229 # on both IPv4 and IPv6 230 unless ($Address =~ /^0.0.0.0$/) { 231 $BindFailed{$Temp}++; 232 } 233 } elsif ( $ThisLine =~ m/^(log: )?Generating .* \w+ key\./ ) { # ssh/openssh 234 # Don't care about this... 235 if ( $Debug >= 5 ) { 236 print STDERR "DEBUG: Found -Generating RSA key- line\n"; 237 } 238 } elsif ( $ThisLine =~ m/^packet_set_maxsize: /) { 239 if ( $Debug >= 5 ) { 240 print STDERR "DEBUG: Found -packet_set_maxsize- line\n"; 241 } 242 } elsif ( $ThisLine =~ m/^(log: )?\w+ key generation complete\./ ) { # ssh/openssh 243 # Don't care about this... 244 if ( $Debug >= 5 ) { 245 print STDERR "DEBUG: Found -Keygen complete- line\n"; 246 } 247 } elsif ( my ($Method,$User,$Host,undef) = ( $ThisLine =~ m/^Failed (\S+) for (\S+) from ([^ ]+) port (\d+)/ ) ) { #openssh 248 # depending on log mode, openssh may not report these in connection context. 249 if ( $Debug >= 5 ) { 250 print STDERR "DEBUG: Found -Failed login- line\n"; 251 } 252 $BadLogins{$Host}{"$User/$Method"}++; 253 } elsif ($ThisLine =~ s/^(log: )?Could not reverse map address ([^ ]*).*$/$2/) { 254 $NoRevMap{$ThisLine}++; 255 } elsif ( my ($Address) = ($ThisLine =~ /^reverse mapping checking getaddrinfo for (\S+( \[\S+\])?) failed - POSSIBLE BREAK-IN ATTEMPT!/)) { 256 $NoRevMap{$Address}++; 257 } elsif ( my ($IP,$Address) = ($ThisLine =~ /^Address ([^ ]*) maps to ([^ ]*), but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT!/)) { 258 $NoRevMap{"$Address($IP)"}++; 259 } elsif ( my (undef,$Address) = ($ThisLine =~ /^warning: ([^ ]*), line \d+: can't verify hostname: getaddrinfo\(([^ ]*), AF_INET\) failed$/)) { 260 $NoRevMap{$Address}++; 261 } elsif ( my (undef,$Addresses) = ($ThisLine =~ /^warning: ([^ ]*), line \d+: host [^ ]* mismatch: (.*)$/)) { 262 $MisMatch{$Addresses}++; 263 } elsif ( $ThisLine =~ m/subsystem request for sftp/ ) { 264 $sftpRequests++; 265 } elsif ( $ThisLine =~ m/refused connect from (.*)$/ ) { 266 $RefusedConnections{$1}++; 267 } elsif ( my ($Reason) = ($ThisLine =~ /^Authentication refused: (.*)$/ ) ) { 268 $RefusedAuthentication{$Reason}++; 269 } elsif ( my (undef,$Host,$Port,$Reason,$Offer) = ($ThisLine =~ /^(fatal: )?Unable to negotiate with ([^ ]+)( port \d+)?: (.*)\. Their offer: (.*) \[preauth\]$/) ) { 270 $NegotiationFailed{$Reason}{$Host}{$Offer}++; 271 } elsif ( my ($Reason,$Host,$Offer) = ($ThisLine =~ /^(Protocol major versions differ) for ([^ ]+)(?: port \d+): (.*)$/) ) { 272 $NegotiationFailed{$Reason}{$Host}{$Offer}++; 273 } elsif ( my ($Prio,$Host,$Port,$Code,$Reason) = ($ThisLine =~ /^(error: )?Received disconnect from ([^ ]*)( port \d+)?: ?(\d+): (.*)$/)) { 274 # Reason 11 ({SSH,SSH2}_DISCONNECT_BY_APPLICATION) is expected, and logged at severity level INFO 275 if (($Reason =~ /preauth/) || ($Code != 11) || ($Detail >= 30)) { 276 $DisconnectReceived{$Reason}{$Host}++; 277 } 278 } elsif ( my ($Host) = ($ThisLine =~ /^ROOT LOGIN REFUSED FROM ([^ ]*)$/)) { 279 $RootLogin{$Host}++; 280 } elsif ( my ($Error) = ($ThisLine =~ /^Cannot release PAM authentication\[\d\]: (.*)$/)) { 281 $PamReleaseFail{$Error}++; 282 } elsif ( my ($Error) = ($ThisLine =~ /^pam_systemd\(sshd:session\): Failed to release session: (.*)$/)) { 283 $PamReleaseFail{$Error}++; 284 } elsif ( my ($Error) = ( $ThisLine =~ m/^error: PAM: (.*)$/)) { 285 $PamError{$Error}++; 286 } elsif ( my ($Error) = ( $ThisLine =~ m/pam_systemd\(sshd:session\): (Failed to create session: .*)$/)) { 287 $PamError{$Error}++; 288 } elsif ( my ($Reason) = ( $ThisLine =~ m/pam_chroot\(.+\):\s+([^:])/)) { 289 $PamChroot{$Reason}++; 290 } elsif ( my ($Error) = ( $ThisLine =~ m/^error: Could not get shadow information for (.*)$/)) { 291 $ShadowInfo{$Error}++; 292 } elsif ( my ($Reason) = ($ThisLine =~ /^Setting tty modes failed: (.*)$/)) { 293 $TTYModesFail{$Reason}++; 294 } elsif ( my ($User,undef) = ($ThisLine =~ /^User ([^ ]*) not allowed because ([^ ]*) exists$/)) { 295 $LoginLock{$User}++; 296 } elsif ( my ($Method,$InvaUser,$IlegUser,$EmptyUser,$User,$Host) = ($ThisLine =~ /^Postponed ([^ ]*) for ((invalid user) [^ ]*|(illegal user) [^ ]*|([^ ]*)) from ([^ ]*) port \d+ ssh/)) { 297 $PostPonedAuth{"$User/$Method"}{$Host}++; 298 if ($IlegUser =~ /illegal user/) {$IllegalUsers{$Host}{$User}++;} 299 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*) not allowed because account is locked/)) { 300 $LockedAccount{$User}++; 301 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*) from (?:[^ ]*) not allowed because not listed in AllowUsers/)) { 302 $AllowUsers{$User}++; 303 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*)( from [0-9.]*)? not allowed because listed in DenyUsers/)){ 304 $DenyUsers{$User}++; 305 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*)( from [0-9.]*)? not allowed because not in any group/)) { 306 $NoGroups{$User}++; 307 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*)( from [^ ]*)? not allowed because a group is listed in DenyGroups/)) { 308 $DenyGroups{$User}++; 309 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*) from ([^ ]*) not allowed because none of user's groups are listed in AllowGroups/)) { 310 $AllowGroups{$User}++; 311 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*) not allowed because shell (\S+) does not exist/)) { 312 $NoShellUsers{$User}++; 313 } elsif ( my ($User) = ($ThisLine =~ /^User ([^ ]*) not allowed because shell (\S+) is not executable/)) { 314 $ShellNotExecutableUsers{$User}++; 315 } elsif ( my ($User) = ($ThisLine =~ /^fatal: Access denied for user ([^ ]+) by PAM account configuration \[preauth\]/)) { 316 $PamDeny{$User}++; 317 } elsif ( my ($IP) = ($ThisLine =~ /^scanned from ([^ ]*)/) ) { 318 push @Scanned, $IP; 319 } elsif ( my (undef,$Line,$Option) = ($ThisLine =~ /^re(xec|process config) line (\d+): Deprecated option (.*)$/)) { 320 $DeprecatedOption{"$Option - line $Line"}++; 321 } elsif ( my ($Pom1,$Pom2,$User) = ($ThisLine =~ /pam_krb5(\[\d*\])?: authentication fails for (`|')([^ ]*)'/)) { 322 $KrbAutFail{$User}++; 323 } elsif ( my ($Error) = ($ThisLine =~ /pam_krb5: authenticate error: (.*)$/)) { 324 $KrbAutErr{$Error}++; 325 } elsif ( ($ThisLine =~ /pam_krb5: unable to determine uid\/gid for user$/)) { 326 $KrbAutErr{"unable to determine uid/gid for user"}++; 327 } elsif ( my ($Error) = ($ThisLine =~ /pam_krb5: error removing file (.*)$/)) { 328 $KrbErr{"error removing file " . $Error}++; 329 } elsif ( my ($Pom,$Error) = ($ThisLine =~ /pam_krb5(\[\d*\]): error resolving user name '[^ ]*' to uid\/gid pai/)) { 330 $KrbErr{"error resolving user name '$Error' to uid\/gid pai"}++; 331 } elsif ( my (undef,$User,$Host) = ($ThisLine =~ m/^(Illegal|Invalid) user (.*) from ([^ ]+)/ )) { 332 $PotentialIllegalUsers{$Host}{$User}++; 333 } elsif ( my (undef,$User) = ($ThisLine =~ /^input_userauth_request: (illegal|invalid) user (.*)$/ )) { 334 if ($User =~ m/(.*) \[preauth\]/) { 335 $User = $1; 336 } 337 $PotentialIllegalUsers{"undef"}{$User}++; 338 } elsif (my ($File,$Perm,$Why) = ($ThisLine =~ /error: chmod (.*) (.*) failed: (.*)/)) { 339 $ChmodErr{"$File,$Perm,$Why"}++; 340 } elsif (my ($File,$From,$To,$Why) = ($ThisLine =~ /error: chown (.*) (.*) (.*) failed: (.*)/)) { 341 $ChownErr{"$File,$From,$To,$Why"}++; 342 } elsif (my ($user,$realm) = ($ThisLine =~ /Authorized to ([^ ]+), krb5 principal \1@([^ ]+) \((?:krb5_kuserok|ssh_gssapi_krb5_cmdok)\)/)) { 343 $Krb_realm{$realm}{$user}++; 344 } elsif (my ($Action,$User) = ($ThisLine =~ /^session ((?:open|clos)ed) for local user (\S+) from /)) { 345 $Session{"Action,$User"}++; 346 } elsif ($ThisLine =~ /^sent status No such file$/) { 347 $StatusNoSuchFile++; 348 } elsif (my ($File,$Flags,$Mode) = ($ThisLine =~ /^open "(.*)" flags (\S+) mode (\d+)/ )) { 349 $OpenFile{"$File:$Flags:$Mode"}++; 350 } elsif (my ($File,$BytesRead,$BytesWritten) = ($ThisLine =~ /^close "(.*)" bytes read (\d+) written (\d+)/ )) { 351 $CloseFileReadWrite{"$File,$BytesRead,$BytesWritten"}++; 352 } elsif (my ($Sent,$Received) = ($ThisLine =~ /^Transferred: sent (\d+), received (\d+) bytes$/ )) { 353 $BytesSent += $Sent; 354 $BytesReceived += $Received; 355 } elsif (my ($File,$Modtime) = ($ThisLine =~ /^set "(.*)" modtime (\d+-\d+:\d+:\d+)$/ )) { 356 $SetModtime{"$Modtime,$File"}++; 357 } elsif (my ($Dir) = ($ThisLine =~ /^opendir "(.*)"$/ )) { 358 $OpenDir{$Dir}++; 359 } elsif (my ($Dir) = ($ThisLine =~ /^closedir "(.*)"$/ )) { 360 $CloseDir{$Dir}++; 361 } elsif (my ($File) = ($ThisLine =~ /^realpath "(.*)"$/ )) { 362 $RealPath{$File}++; 363 } elsif (my ($File) = ($ThisLine =~ /^stat name "(.*)"$/ )) { 364 $Stat{$File}++; 365 } elsif (my ($Dir) = ($ThisLine =~ /^Changed root directory to "(.*)"/ )) { 366 $Chroot{$Dir}++; 367 } elsif (my ($ClientVer) = ($ThisLine =~ /^received client version (\S+)/ )) { 368 $ClientVers{$ClientVer}++; 369 } elsif (my ($Host,$Port) = ($ThisLine =~ /^error: connect_to (\S+) port (\d+): failed\.$/)) { 370 $ConnectFailed{"$Host port $Port"}++; 371 } else { 372 # Report any unmatched entries... 373 unless ($ThisLine =~ /fwd X11 connect/) { 374 $OtherList{$ThisLine} += 1; 375 } 376 } 377} 378 379########################################################### 380 381foreach my $Host (sort keys %PotentialIllegalUsers) { 382 foreach my $User (sort keys %{$PotentialIllegalUsers{$Host}}) { 383 my @user_hosts = grep { $PotentialIllegalUsers{$_}{$User} } keys %PotentialIllegalUsers; 384 385 if ($Host eq "undef") { 386 if ((scalar @user_hosts) == 1 && $user_hosts[0] == "undef") { 387 # Report illegal user from "undef" only if there are no other hosts 388 # for the given user 389 $IllegalUsers{"undef"}{$User}++; 390 } 391 } 392 else { 393 while ($IllegalUsers{$Host}{$User} < $PotentialIllegalUsers{$Host}{$User}) { 394 $IllegalUsers{$Host}{$User}++; 395 } 396 } 397 } 398} 399 400########################################################### 401 402sub timesplural { 403 my ($count) = @_; 404 my $plural = ($count > 1) ? "s" : ""; 405 return "$count Time$plural\n"; 406} 407 408if ($NetworkErrors) { 409 print "\nNetwork Read Write Errors: " . $NetworkErrors . "\n"; 410} 411if ($Kills) { 412 print "\nSSHD Killed: " . timesplural($Kills); 413} 414if ($Starts) { 415 print "\nSSHD Started: " . timesplural($Starts); 416} 417 418if (keys %DeprecatedOption) { 419 print "\nDeprecated options in SSH config:\n"; 420 foreach my $Option (sort {$a cmp $b} keys %DeprecatedOption) { 421 print " $Option\n"; 422 } 423} 424 425if (keys %RootLogin) { 426 print "\n\nWARNING!!!\n"; 427 print "Refused ROOT login attempt from:\n"; 428 foreach my $Host (sort {$a cmp $b} keys %RootLogin) { 429 print " $Host : " . timesplural($RootLogin{$Host}); 430 } 431} 432 433if (keys %BindFailed) { 434 print "\nFailed to bind:\n"; 435 foreach my $ThisOne (sort {$a cmp $b} keys %BindFailed) { 436 print " $ThisOne : " . timesplural($BindFailed{$ThisOne}); 437 } 438} 439 440if ($Detail >= 30 && keys %ConnectFailed) { 441 # SSH Socks Forwarding 442 print "\nFailed to connect to:\n"; 443 foreach my $ThisOne (sort {$a cmp $b} keys %ConnectFailed) { 444 print " $ThisOne : " . timesplural($ConnectFailed{$ThisOne}); 445 } 446} 447 448if ($Detail >= 10) { 449 if (keys %NoRevMap) { 450 print "\nCouldn't resolve these IPs:\n"; 451 foreach my $ThisOne (sort {$a cmp $b} keys %NoRevMap) { 452 print " $ThisOne: " . timesplural($NoRevMap{$ThisOne}); 453 } 454 } 455 if (keys %NoIdent) { 456 print "\nDidn't receive an ident from these IPs:\n"; 457 foreach my $ThisOne (sort {$a cmp $b} keys %NoIdent) { 458 print " $ThisOne: " . timesplural($NoIdent{$ThisOne}); 459 } 460 } 461 if (keys %MisMatch) { 462 print "\nMismatched host names and/or IPs:\n"; 463 foreach my $ThisOne (sort keys %MisMatch) { 464 print " $ThisOne: " . timesplural($MisMatch{$ThisOne}); 465 } 466 } 467} 468 469if (keys %NegotiationFailed) { 470 print "\nNegotiation failed:\n"; 471 foreach my $Reason (sort {$a cmp $b} keys %NegotiationFailed) { 472 my $Total = 0; 473 print " $Reason"; 474 if ( $Detail > 0 ) { 475 print "\n"; 476 } 477 foreach my $Host (sort {$a cmp $b} keys %{$NegotiationFailed{$Reason}}) { 478 my $HostTotal = 0; 479 foreach my $Offer (sort {$a cmp $b} keys %{$NegotiationFailed{$Reason}{$Host}}) { 480 $HostTotal += $NegotiationFailed{$Reason}{$Host}{$Offer}; 481 } 482 $Total += $HostTotal; 483 if ( $Detail > 0 ) { 484 print " $Host: " . timesplural($HostTotal); 485 } 486 if ( $Detail > 5 ) { 487 foreach my $Offer (sort {$a cmp $b} keys %{$NegotiationFailed{$Reason}{$Host}}) { 488 my $tot = $NegotiationFailed{$Reason}{$Host}{$Offer}; 489 print " $Offer: " . timesplural($tot); 490 } 491 } 492 } 493 if ( $Detail == 0 ) { 494 print ": " . timesplural($Total); 495 } 496 } 497} 498 499if (keys %TooManyFailures) { 500 print "\nDisconnecting after too many authentication failures for user:\n"; 501 foreach my $User (sort {$a cmp $b} keys %TooManyFailures) { 502 print " $User : " . timesplural($TooManyFailures{$User}); 503 } 504} 505 506if (keys %BadLogins) { 507 print "\nFailed logins from:\n"; 508 foreach my $ip (sort SortIP keys %BadLogins) { 509 my $name = LookupIP($ip); 510 my $totcount = 0; 511 foreach my $user (keys %{$BadLogins{$ip}}) { 512 $totcount += $BadLogins{$ip}{$user}; 513 } 514 print " $name: ". timesplural($totcount); 515 if ($Detail >= 5) { 516 my $sort = CountOrder(%{$BadLogins{$ip}}); 517 foreach my $user (sort $sort keys %{$BadLogins{$ip}}) { 518 my $val = $BadLogins{$ip}{$user}; 519 print " $user: " . timesplural($val); 520 } 521 } 522 } 523} 524 525if (keys %IllegalUsers) { 526 print "\nIllegal users from:\n"; 527 foreach my $ip (sort SortIP keys %IllegalUsers) { 528 my $name = LookupIP($ip); 529 my $totcount = 0; 530 foreach my $user (keys %{$IllegalUsers{$ip}}) { 531 $totcount += $IllegalUsers{$ip}{$user}; 532 } 533 print " $name: " . timesplural($totcount); 534 if ($Detail >= 5) { 535 my $sort = CountOrder(%{$IllegalUsers{$ip}}); 536 foreach my $user (sort $sort keys %{$IllegalUsers{$ip}}) { 537 my $val = $IllegalUsers{$ip}{$user}; 538 print " $user: " . timesplural($val); 539 } 540 } 541 } 542} 543 544if (keys %LockedAccount) { 545 print "\nLocked account login attempts:\n"; 546 foreach my $User (sort {$a cmp $b} keys %LockedAccount) { 547 print " $User : " . timesplural($LockedAccount{$User}); 548 } 549} 550 551if (keys %AllowUsers) { 552 print "\nLogin attempted when not in AllowUsers list:\n"; 553 foreach my $User (sort {$a cmp $b} keys %AllowUsers) { 554 print " $User : " . timesplural($AllowUsers{$User}); 555 } 556} 557 558if (keys %DenyUsers) { 559 print "\nLogin attempted when in DenyUsers list:\n"; 560 foreach my $User (sort {$a cmp $b} keys %DenyUsers) { 561 print " $User : . " . timesplural($DenyUsers{$User}); 562 } 563} 564 565if (keys %AllowGroups) { 566 print "\nLogin attempted when not in AllowGroups list:\n"; 567 foreach my $User (sort {$a cmp $b} keys %AllowGroups) { 568 print " $User : " . timesplural($AllowGroups{$User}); 569 } 570} 571 572if (keys %DenyGroups) { 573 print "\nLogin attempted when in DenyGroups list:\n"; 574 foreach my $User (sort {$a cmp $b} keys %DenyGroups) { 575 print " $User : " . timesplural($DenyGroups{$User}); 576 } 577} 578 579if (keys %PamDeny) { 580 print "\nLogin attempted when denied by PAM configuration:\n"; 581 foreach my $User (sort {$a cmp $b} keys %PamDeny) { 582 print " $User : " . timesplural($PamDeny{$User}); 583 } 584} 585 586if (keys %NoGroups) { 587 print "\nLogin attempted when user is in no group:\n"; 588 foreach my $User (sort {$a cmp $b} keys %NoGroups) { 589 print " $User : " . timesplural($NoGroups{$User}); 590 } 591} 592 593if (keys %NoShellUsers) { 594 print "\nLogin attempted when shell does not exist:\n"; 595 foreach my $User (sort {$a cmp $b} keys %NoShellUsers) { 596 print " $User : " . timesplural($NoShellUsers{$User}); 597 } 598} 599 600if (keys %ShellNotExecutableUsers) { 601 print "\nLogin attempted when shell is not executable:\n"; 602 foreach my $User (sort {$a cmp $b} keys %ShellNotExecutableUsers) { 603 print " $User : " . timesplural($ShellNotExecutableUsers{$User}); 604 } 605} 606 607if ((keys %LoginLock) and ($Detail >= 5)) { 608 print "\nUser login attempt when nologin was set:\n"; 609 foreach my $User (sort {$a cmp $b} keys %LoginLock) { 610 print " $User : " . timesplural($LoginLock{$User}); 611 } 612} 613 614if (keys %PostPonedAuth) { 615 print "\nPostponed authentication:\n"; 616 foreach my $User (sort {$a cmp $b} keys %PostPonedAuth) { 617 print " $User:\n"; 618 foreach my $Host (sort {$a cmp $b} keys %{$PostPonedAuth{$User}}) { 619 print " $Host: " . timesplural($PostPonedAuth{$User}{$Host}); 620 } 621 } 622} 623 624if (keys %Users) { 625 print "\nUsers logging in through sshd:\n"; 626 foreach my $user (sort {$a cmp $b} keys %Users) { 627 print " $user:\n"; 628 if ($Detail < 20) { 629 my $totalSort = TotalCountOrder(%{$Users{$user}}, \&SortIP); 630 foreach my $ip (sort $totalSort keys %{$Users{$user}}) { 631 my $name = LookupIP($ip); 632 my $val = (values %{$Users{$user}{$ip}})[0]; 633 print " $name: " . timesplural($val); 634 } 635 } else { 636 my %Methods = (); 637 foreach my $ip (keys %{$Users{$user}}) { 638 foreach my $method (keys %{$Users{$user}{$ip}}) { 639 $Methods{$method}{$ip} = $Users{$user}{$ip}{$method}; 640 } 641 } 642 if (scalar keys %{$Users{$user}} < scalar %Methods) { 643 my $totalSort = TotalCountOrder(%{$Users{$user}}, \&SortIP); 644 foreach my $ip (sort $totalSort keys %{$Users{$user}}) { 645 my $name = LookupIP($ip); 646 print " $name:\n"; 647 my $sort = CountOrder(%{$Users{$user}{$ip}}); 648 foreach my $method (sort $sort keys %{$Users{$user}{$ip}}) { 649 my $val = $Users{$user}{$ip}{$method}; 650 print " $method: " . timesplural($val); 651 } 652 } 653 } else { 654 my $totalSort = TotalCountOrder(%Methods); 655 foreach my $method (sort $totalSort keys %Methods) { 656 print " $method:\n"; 657 my $sort = CountOrder(%{$Methods{$method}}); 658 foreach my $ip (sort $sort keys %{$Methods{$method}}) { 659 my $name = LookupIP($ip); 660 my $val = (values %{$Users{$user}{$ip}})[0]; 661 print " $name: " . timesplural($val); 662 } 663 } 664 } 665 } 666 } 667} 668 669if (keys %RefusedAuthentication) { 670 print "\nAuthentication refused:\n"; 671 foreach my $Reason (sort {$a cmp $b} keys %RefusedAuthentication) { 672 print " $Reason : " . timesplural($RefusedAuthentication{$Reason}); 673 } 674} 675 676if (keys %KrbAutFail) { 677 print "\n\Failed pam_krb5 authentication:\n"; 678 foreach my $User (sort keys %KrbAutFail) { 679 print " $User: " . timesplural($KrbAutFail{$User}); 680 } 681} 682 683if (keys %KrbAutErr) { 684 print "\n\pam_krb5 authentication errors:\n"; 685 foreach my $Error (sort keys %KrbAutErr) { 686 print " $Error: " . timesplural($KrbAutErr{$Error}); 687 } 688} 689 690 691if (keys %KrbErr) { 692 print "\n pam_krb5 errors:\n"; 693 foreach my $Error (sort keys %KrbErr) { 694 print " $Error: " . timesplural($KrbErr{$Error}); 695 } 696} 697 698 699if (keys %DisconnectReceived) { 700 print "\nReceived disconnect:\n"; 701 foreach my $Reason (sort {$a cmp $b} keys %DisconnectReceived) { 702 my $Total = 0; 703 print " $Reason"; 704 foreach my $Host (sort {$a cmp $b} keys %{$DisconnectReceived{$Reason}}) { 705 $Total += $DisconnectReceived{$Reason}{$Host}; 706 if( $Detail > 0 ) { 707 print "\n $Host : $DisconnectReceived{$Reason}{$Host} Time(s)"; 708 } 709 } 710 if( $Detail > 0 ) { 711 print "\n"; 712 } else { 713 print " : " . timesplural($Total); 714 } 715 } 716} 717 718if ($#Scanned >= 0) { 719 print "\nScanned from:\n"; 720 foreach my $ThisOne (sort SortIP @Scanned) { 721 print " " . LookupIP($ThisOne) . "\n"; 722 } 723} 724 725if (keys %RefusedConnections) { 726 my $output; 727 foreach my $badguy (sort {$a cmp $b} keys %RefusedConnections ) { 728 if ($RefusedConnectionsThreshold == 0 || $Detail > 5 || $RefusedConnections{$badguy} >= $RefusedConnectionsThreshold) { 729 $output .= " $badguy: " . timesplural($RefusedConnections{$badguy}); 730 } 731 } 732 if ($output ne '') { 733 print "\nRefused incoming connections:\n"; 734 print $output; 735 } 736} 737 738if (keys %PamReleaseFail) { 739 print "\nCannot release PAM authentication:\n"; 740 foreach my $Error (sort {$a cmp $b} keys %PamReleaseFail) { 741 print " $Error : " . timesplural($PamReleaseFail{$Error}); 742 } 743} 744 745if (keys %ShadowInfo) { 746 print "\nCould not get shadow information for:\n"; 747 foreach my $Error (sort {$a cmp $b} keys %ShadowInfo) { 748 print " $Error : " . timesplural($ShadowInfo{$Error}); 749 } 750} 751 752if (keys %PamError) { 753 print "\nError in PAM authentication:\n"; 754 foreach my $Error (sort {$a cmp $b} keys %PamError) { 755 print " $Error : " . timesplural($PamError{$Error}); 756 } 757} 758 759if (keys %PamChroot) { 760 print "\nPAM chroot:\n"; 761 foreach my $Reason (sort {$a cmp $b} keys %PamChroot) { 762 print " $Reason : " . timesplural($PamChroot{$Reason}); 763 } 764} 765 766if (keys %TTYModesFail) { 767 print "\nSetting tty modes failed:\n"; 768 foreach my $Reason (sort {$a cmp $b} keys %TTYModesFail) { 769 print " $Reason : " . timesplural($TTYModesFail{$Reason}); 770 } 771} 772 773if ($sftpRequests > 0) { 774 print "\nSFTP subsystem requests: $sftpRequests Time(s)\n"; 775 if ($Detail >= 50) { 776 foreach my $root (sort {$a cmp $b} keys %Chroot) { 777 print " Chroot: $root : " . timesplural($Chroot{$root}); 778 } 779 } 780 if ($Detail >= 0) { 781 foreach my $dir (sort {$a cmp $b} keys %CloseDir) { 782 if ($CloseDir{$dir} != $OpenDir{$dir} || $Detail >= 40) { 783 print " Directory $dir: opened/closed $OpenDir{$dir}/$CloseDir{$dir} Time(s)\n"; 784 # Note: this number might not match if the open/closes span log windows..." 785 } 786 } 787 } 788 if ($Detail >= 30) { 789 foreach my $details (sort {$a cmp $b} keys %CloseFileReadWrite) { 790 if (my ($file,$read,$written) = ($details =~ /^(.*),(\d+),(\d+)$/)) { 791 if ($read || $written || $Detail >= 40) { 792 print " Read/Wrote $read/$written byte(s) from file $file\n"; 793 } 794 } 795 } 796 } 797 if ($Detail >= 60) { 798 foreach my $details (sort {$a cmp $b} keys %OpenFile) { 799 if (my ($file,$flags,$mode) = ($details =~ /^(.*):([^:]*):(\d+)$/)) { 800 print " Opened (mode: $mode, flags: $flags) file $file\n"; 801 } 802 } 803 } 804 if ($Detail >= 70) { 805 foreach my $path (sort {$a cmp $b} keys %RealPath) { 806 print " Realpath $path\n"; 807 } 808 } 809 if ($Detail >= 50) { 810 foreach my $details (sort {$a cmp $b} keys %SetModtime) { 811 if (my ($modtime,$file) = ($details =~ /^([^,]*),(.*)$/)) { 812 print " Set modtime $modtime for file $file\n"; 813 } 814 } 815 } 816 if ($Detail >= 70) { 817 foreach my $path (sort {$a cmp $b} keys %Stat) { 818 print " Stat: $path : " . timesplural($Stat{$path}); 819 } 820 } 821 if ($Detail >= 40) { 822 if ($StatusNoSuchFile) { 823 print " Sent status No such file: " . timesplural($StatusNoSuchFile); 824 } 825 } 826 if ($Detail >= 0) { 827 if ($BytesSent || $BytesReceived) { 828 print " Transferred: sent/received $BytesSent/$BytesReceived byte(s)\n"; 829 } 830 } 831 if ($Detail >= 50) { 832 foreach my $client (sort {$a cmp $b} keys %ClientVers) { 833 print " Client version $client connected " . timesplural($ClientVers{$client}); 834 } 835 } 836} 837 838if (keys %ChmodErr) { 839 print "\nChmod errors:\n"; 840 foreach (sort keys %ChmodErr) { 841 my ($File,$Perm,$Why)= split ","; 842 print " " . $File . " " . $Perm . " failed(" . $Why . "): " . timesplural($ChmodErr{"$File,$Perm,$Why"}); 843 } 844} 845 846if (keys %ChownErr) { 847 print "\nChown errors:\n"; 848 foreach (keys %ChownErr) { 849 my ($File,$From,$To,$Why)= split ","; 850 print " " . $File . " " . $From . " " .$To . " failed(" . $Why . "): " . timesplural($ChmodErr{"$File,$From,$To,$Why"}); 851 } 852} 853 854if ( ($Detail == 7 && keys %Krb_realm > 1) || ($Detail > 8 && keys %Krb_realm) ){ 855 print "\nSuccessful Kerberos Authentication from ",(scalar keys %Krb_realm)," realm:\n"; 856 foreach my $realm (sort keys %Krb_realm) { 857 if($Detail > 9){ 858 print " ",$realm,":\n"; 859 foreach my $user(sort keys %{$Krb_realm{$realm}}){ 860 print " ",$user,": " . timesplural($Krb_realm{$realm}{$user}); 861 } 862 }else{ 863 print " ",$realm,": ". (scalar keys %{$Krb_realm{$realm}}) . " User(s)\n"; 864 } 865 } 866} 867 868if (keys %OtherList) { 869 print "\n**Unmatched Entries**\n"; 870 print "$_ : " . timesplural($OtherList{$_}) foreach sort keys %OtherList; 871} 872 873exit(0); 874 875# vi: shiftwidth=3 tabstop=3 syntax=perl et 876# Local Variables: 877# mode: perl 878# perl-indent-level: 3 879# indent-tabs-mode: nil 880# End: 881