1#! /usr/pkg/bin/perl
2# -*- mode: perl; perl-indent-level: 8 -*-
3#
4# Copyright (c) 2003 Kungliga Tekniska Högskolan
5# (Royal Institute of Technology, Stockholm, Sweden).
6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11#
12# 1. Redistributions of source code must retain the above copyright
13#    notice, this list of conditions and the following disclaimer.
14#
15# 2. Redistributions in binary form must reproduce the above copyright
16#    notice, this list of conditions and the following disclaimer in the
17#    documentation and/or other materials provided with the distribution.
18#
19# 3. Neither the name of the Institute nor the names of its contributors
20#    may be used to endorse or promote products derived from this software
21#    without specific prior written permission.
22#
23# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26# ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33# SUCH DAMAGE.
34#
35# Id
36#
37# kdc-log-analyze - Analyze a KDC log file and give a report on the contents
38#
39# Note: The parts you want likely want to customize are the variable $notlocal,
40# the array @local_network_re and the array @local_realms.
41#
42# Idea and implemetion for MIT Kerberos was done first by
43# Ken Hornstein <kenh@cmf.nrl.navy.mil>, this program wouldn't exists
44# without his help.
45#
46
47use strict;
48use Sys::Hostname;
49
50my $notlocal = 'not SU';
51my @local_realms = ( "SU.SE" );
52my @local_networks_re =
53    (
54      "130\.237",
55      "193\.11\.3[0-9]\.",
56      "130.242.128",
57      "2001:6b0:5:"
58      );
59
60my $as_req = 0;
61my %as_req_addr;
62my %as_req_addr_nonlocal;
63my %as_req_client;
64my %as_req_server;
65my %addr_uses_des;
66my %princ_uses_des;
67my $five24_req = 0;
68my %five24_req_addr;
69my %five24_req_addr_nonlocal;
70my %five24_req_server;
71my %five24_req_client;
72my $as_req_successful = 0;
73my $as_req_error = 0;
74my $no_such_princ = 0;
75my %no_such_princ_princ;
76my %no_such_princ_addr;
77my %no_such_princ_addr_nonlocal;
78my $as_req_etype_odd = 0;
79my %bw_addr;
80my $pa_alt_princ_request = 0;
81my $pa_alt_princ_verify = 0;
82my $tgs_req = 0;
83my %tgs_req_addr;
84my %tgs_req_addr_nonlocal;
85my %tgs_req_client;
86my %tgs_req_server;
87my $tgs_xrealm_out = 0;
88my %tgs_xrealm_out_realm;
89my %tgs_xrealm_out_princ;
90my $tgs_xrealm_in = 0;
91my %tgs_xrealm_in_realm;
92my %tgs_xrealm_in_princ;
93my %enctype_session;
94my %enctype_ticket;
95my $restarts = 0;
96my $forward_non_forward = 0;
97my $v4_req = 0;
98my %v4_req_addr;
99my %v4_req_addr_nonlocal;
100my $v4_cross = 0;
101my %v4_cross_realm;
102my $v5_cross = 0;
103my %v5_cross_realm;
104my $referrals = 0;
105my %referral_princ;
106my %referral_realm;
107my %strange_tcp_data;
108my $http_malformed = 0;
109my %http_malformed_addr;
110my $http_non_kdc = 0;
111my %http_non_kdc_addr;
112my $tcp_conn_timeout = 0;
113my %tcp_conn_timeout_addr;
114my $failed_processing = 0;
115my %failed_processing_addr;
116my $connection_closed = 0;
117my %connection_closed_addr;
118my $pa_failed = 0;
119my %pa_failed_princ;
120my %pa_failed_addr;
121my %ip;
122
123$ip{'4'} = $ip{'6'} = 0;
124
125while (<>) {
126          process_line($_);
127}
128
129print "Kerberos KDC Log Report for ",
130    hostname, " on ", scalar localtime, "\n\n";
131
132print "General Statistics\n\n";
133
134print "\tNumber of IPv4 requests: $ip{'4'}\n";
135print "\tNumber of IPv6 requests: $ip{'6'}\n\n";
136
137print "\tNumber of restarts: $restarts\n";
138print "\tNumber of V4 requests: $v4_req\n";
139if ($v4_req > 0) {
140          print "\tTop ten IP addresses performing V4 requests:\n";
141          topten(\%v4_req_addr);
142}
143if (int(keys %v4_req_addr_nonlocal) > 0) {
144          print "\tTop ten $notlocal IP addresses performing V4 requests:\n";
145          topten(\%v4_req_addr_nonlocal);
146
147}
148print "\n";
149
150print "\tNumber of V4 cross realms (krb4 and 524) requests: $v4_cross\n";
151if ($v4_cross > 0) {
152          print "\tTop ten realms performing V4 cross requests:\n";
153          topten(\%v4_cross_realm);
154}
155print "\n";
156
157print "\tNumber of V45 cross realms requests: $v5_cross\n";
158if ($v5_cross > 0) {
159          print "\tTop ten realms performing V4 cross requests:\n";
160          topten(\%v5_cross_realm);
161}
162print "\n";
163
164print "\tNumber of failed lookups: $no_such_princ\n";
165if ($no_such_princ > 0) {
166          print "\tTop ten IP addresses failing to find principal:\n";
167          topten(\%no_such_princ_addr);
168          print "\tTop ten $notlocal IP addresses failing find principal:\n";
169          topten(\%no_such_princ_addr_nonlocal);
170          print "\tTop ten failed to find principals\n";
171          topten(\%no_such_princ_princ);
172}
173print "\n";
174
175print "\tBandwidth pigs:\n";
176topten(\%bw_addr);
177print "\n";
178
179print "\tStrange TCP data clients: ", int(keys %strange_tcp_data),"\n";
180topten(\%strange_tcp_data);
181print "\n";
182
183print "\tTimeout waiting on TCP requests: ", $tcp_conn_timeout,"\n";
184if ($tcp_conn_timeout > 0) {
185          print "\tTop ten TCP timeout request clients\n";
186          topten(\%tcp_conn_timeout_addr);
187}
188print "\n";
189
190print "\tFailed processing requests: ", $failed_processing,"\n";
191if ($failed_processing > 0) {
192          print "\tTop ten failed processing request clients\n";
193          topten(\%failed_processing_addr);
194}
195print "\n";
196
197print "\tConnection closed requests: ", $connection_closed,"\n";
198if ($connection_closed > 0) {
199          print "\tTop ten connection closed request clients\n";
200          topten(\%connection_closed_addr);
201}
202print "\n";
203
204print "\tMalformed HTTP requests: ", $http_malformed,"\n";
205if ($http_malformed > 0) {
206          print "\tTop ten malformed HTTP request clients\n";
207          topten(\%http_malformed_addr);
208}
209print "\n";
210
211print "\tHTTP non kdc requests: ", $http_non_kdc,"\n";
212if ($http_non_kdc > 0) {
213          print "\tTop ten HTTP non KDC request clients\n";
214          topten(\%http_non_kdc_addr);
215}
216print "\n";
217
218print "Report on AS_REQ requests\n\n";
219print "Overall AS_REQ statistics\n\n";
220
221print "\tTotal number: $as_req\n";
222
223print "\nAS_REQ client/server statistics\n\n";
224
225print "\tDistinct IP Addresses performing requests: ",
226    int(keys %as_req_addr),"\n";
227print "\tOverall top ten IP addresses\n";
228topten(\%as_req_addr);
229
230print "\tDistinct non-local ($notlocal) IP Addresses performing requests: ",
231                                                  int(keys %as_req_addr_nonlocal), "\n";
232print "\tTop ten non-local ($notlocal) IP address:\n";
233topten(\%as_req_addr_nonlocal);
234
235print "\n\tPreauth failed for for: ", $pa_failed, " requests\n";
236if ($pa_failed) {
237          print "\tPreauth failed top ten IP addresses:\n";
238          topten(\%pa_failed_addr);
239          print "\tPreauth failed top ten principals:\n";
240          topten(\%pa_failed_princ);
241}
242
243print "\n\tDistinct clients performing requests: ",
244    int(keys %as_req_client), "\n";
245print "\tTop ten clients:\n";
246topten(\%as_req_client);
247
248print "\tDistinct services requested: ", int(keys %as_req_server), "\n";
249print "\tTop ten requested services:\n";
250topten(\%as_req_server);
251
252print "\n\n\nReport on TGS_REQ requests:\n\n";
253print "Overall TGS_REQ statistics\n\n";
254print "\tTotal number: $tgs_req\n";
255
256print "\nTGS_REQ client/server statistics\n\n";
257print "\tDistinct IP addresses performing requests: ",
258                                        int(keys %tgs_req_addr), "\n";
259print "\tOverall top ten IP addresses\n";
260topten(\%tgs_req_addr);
261
262print "\tDistinct non-local ($notlocal) IP Addresses performing requests: ",
263                                        int(keys %tgs_req_addr_nonlocal), "\n";
264print "\tTop ten non-local ($notlocal) IP address:\n";
265topten(\%tgs_req_addr_nonlocal);
266
267print "\tDistinct clients performing requests: ",
268                                        int(keys %tgs_req_client), "\n";
269print "\tTop ten clients:\n";
270topten(\%tgs_req_client);
271
272print "\tDistinct services requested: ", int(keys %tgs_req_server), "\n";
273print "\tTop ten requested services:\n";
274topten(\%tgs_req_server);
275
276print "\n\n\nReport on 524_REQ requests:\n\n";
277
278print "\t524_REQ client/server statistics\n\n";
279
280print "\tDistinct IP Addresses performing requests: ",
281    int(keys %five24_req_addr),"\n";
282print "\tOverall top ten IP addresses\n";
283topten(\%five24_req_addr);
284
285print "\tDistinct non-local ($notlocal) IP Addresses performing requests: ",
286                                                  int(keys %five24_req_addr_nonlocal), "\n";
287print "\tTop ten non-local ($notlocal) IP address:\n";
288topten(\%five24_req_addr_nonlocal);
289
290print "\tDistinct clients performing requests: ", int(keys %five24_req_client), "\n";
291print "\tTop ten clients:\n";
292topten(\%five24_req_client);
293
294print "\tDistinct services requested: ", int(keys %five24_req_server), "\n";
295print "\tTop ten requested services:\n";
296topten(\%five24_req_server);
297print "\n";
298
299print "Cross realm statistics\n\n";
300
301print "\tNumber of cross-realm tgs out: $tgs_xrealm_out\n";
302if ($tgs_xrealm_out > 0) {
303          print "\tTop ten realms used for out cross-realm:\n";
304          topten(\%tgs_xrealm_out_realm);
305          print "\tTop ten principals use out cross-realm:\n";
306          topten(\%tgs_xrealm_out_princ);
307}
308print "\tNumber of cross-realm tgs in: $tgs_xrealm_in\n";
309if ($tgs_xrealm_in > 0) {
310          print "\tTop ten realms used for in cross-realm:\n";
311          topten(\%tgs_xrealm_in_realm);
312          print "\tTop ten principals use in cross-realm:\n";
313          topten(\%tgs_xrealm_in_princ);
314}
315
316print "\n\nReport on referral:\n\n";
317
318print "\tNumber of referrals: $referrals\n";
319if ($referrals > 0) {
320          print "\tTop ten referral-ed principals:\n";
321          topten(\%referral_princ);
322          print "\tTop ten to realm referrals:\n";
323          topten(\%referral_realm);
324}
325
326print "\n\nEnctype Statistics:\n\n";
327print "\tTop ten session enctypes:\n";
328topten(\%enctype_session);
329print "\tTop ten ticket enctypes:\n";
330topten(\%enctype_ticket);
331
332print "\tDistinct IP addresses using DES: ", int(keys %addr_uses_des), "\n";
333print "\tTop IP addresses using DES:\n";
334topten(\%addr_uses_des);
335print "\tDistinct principals using DES: ", int(keys %princ_uses_des), "\n";
336print "\tTop ten principals using DES:\n";
337topten(\%princ_uses_des);
338
339print "\n";
340
341printf("Requests to forward non-forwardable ticket: $forward_non_forward\n");
342
343
344exit 0;
345
346my $last_addr = "";
347my $last_principal = "";
348
349sub process_line {
350          local($_) = @_;
351          #
352          # Eat these lines that are output as a result of startup (but
353          # log the number of restarts)
354          #
355          if (/AS-REQ \(krb4\) (.*) from IPv([46]):([0-9\.:a-fA-F]+) for krbtgt.*$/){
356                    $v4_req++;
357                    $v4_req_addr{$3}++;
358                    $v4_req_addr_nonlocal{$3}++ if (!islocaladdr($3));
359                    $last_addr = $3;
360                    $last_principal = $1;
361                    $ip{$2}++;
362          } elsif (/AS-REQ (.*) from IPv([46]):([0-9\.:a-fA-F]+) for (.*)$/) {
363                    $as_req++;
364                    $as_req_client{$1}++;
365                    $as_req_server{$4}++;
366                    $as_req_addr{$3}++;
367                    $as_req_addr_nonlocal{$3}++ if (!islocaladdr($3));
368                    $last_addr = $3;
369                    $last_principal = $1;
370                    $ip{$2}++;
371          } elsif (/TGS-REQ \(krb4\)/) {
372                    #Nothing
373          } elsif (/TGS-REQ (.+) from IPv([46]):([0-9\.:a-fA-F]+) for (.*?)( \[.*\]){0,1}$/) {
374                    $tgs_req++;
375                    $tgs_req_client{$1}++;
376                    $tgs_req_server{$4}++;
377                    $tgs_req_addr{$3}++;
378                    $tgs_req_addr_nonlocal{$3}++ if (!islocaladdr($3));
379                    $last_addr = $3;
380                    $last_principal = $1;
381                    $ip{$2}++;
382
383                    my $source = $1;
384                    my $dest = $4;
385
386                    if (!islocalrealm($source)) {
387                              $tgs_xrealm_in++;
388                              $tgs_xrealm_in_princ{$source}++;
389                              if ($source =~ /[^@]+@([^@]+)/ ) {
390                                        $tgs_xrealm_in_realm{$1}++;
391                              }
392                    }
393                    if ($dest =~ /krbtgt\/([^@]+)@[^@]+/) {
394                              if (!islocalrealm($1)) {
395                                        $tgs_xrealm_out++;
396                                        $tgs_xrealm_out_realm{$1}++;
397                                        $tgs_xrealm_out_princ{$source}++;
398                              }
399                    }
400          } elsif (/524-REQ (.*) from IPv([46]):([0-9\.:a-fA-F]+) for (.*)$/) {
401                    $five24_req++;
402                    $five24_req_client{$1}++;
403                    $five24_req_server{$4}++;
404                    $five24_req_addr{$3}++;
405                    $five24_req_addr_nonlocal{$3}++ if (!islocaladdr($3));
406                    $last_addr = $3;
407                    $last_principal = $1;
408                    $ip{$2}++;
409          } elsif (/TCP data of strange type from IPv[46]:([0-9\.:a-fA-F]+)/) {
410                    $strange_tcp_data{$1}++;
411          } elsif (/Lookup (.*) failed: No such entry in the database/) {
412                    $no_such_princ++;
413                    $no_such_princ_addr{$last_addr}++;
414                    $no_such_princ_addr_nonlocal{$last_addr}++ if (!islocaladdr($last_addr));
415                    $no_such_princ_princ{$1}++;
416          } elsif (/Lookup .* succeeded$/) {
417                    # Nothing
418          } elsif (/Malformed HTTP request from IPv[46]:([0-9\.:a-fA-F]+)$/) {
419                    $http_malformed++;
420                    $http_malformed_addr{$1}++;
421          } elsif (/TCP-connection from IPv[46]:([0-9\.:a-fA-F]+) expired after [0-9]+ bytes/) {
422                    $tcp_conn_timeout++;
423                    $tcp_conn_timeout_addr{$1}++;
424          } elsif (/Failed processing [0-9]+ byte request from IPv[46]:([0-9\.:a-fA-F]+)/) {
425                    $failed_processing++;
426                    $failed_processing_addr{$1}++;
427          } elsif (/connection closed before end of data after [0-9]+ bytes from IPv[46]:([0-9\.:a-fA-F]+)/) {
428                    $connection_closed++;
429                    $connection_closed_addr{$1}++;
430          } elsif (/HTTP request from IPv[46]:([0-9\.:a-fA-F]+) is non KDC request/) {
431                    $http_non_kdc++;
432                    $http_non_kdc_addr{$1}++;
433          } elsif (/returning a referral to realm (.*) for server (.*) that was not found/) {
434                    $referrals++;
435                    $referral_princ{$2}++;
436                    $referral_realm{$1}++;
437          } elsif (/krb4 Cross-realm (.*) -> (.*) disabled/) {
438                    $v4_cross++;
439                    $v4_cross_realm{$1."->".$2}++;
440          } elsif (/524 cross-realm (.*) -> (.*) disabled/) {
441                    $v4_cross++;
442                    $v4_cross_realm{$1."->".$2}++;
443          } elsif (/cross-realm (.*) -> (.*): no transit through realm (.*)/) {
444          } elsif (/cross-realm (.*) -> (.*) via \[([^\]]+)\]/) {
445                    $v5_cross++;
446                    $v5_cross_realm{$1."->".$2}++;
447          } elsif (/cross-realm (.*) -> (.*)/) {
448                    $v5_cross++;
449                    $v5_cross_realm{$1."->".$2}++;
450          } elsif (/sending ([0-9]+) bytes to IPv[46]:([0-9\.:a-fA-F]+)/) {
451                    $bw_addr{$2} += $1;
452          } elsif (/Using ([-a-z0-9]+)\/([-a-z0-9]+)/) {
453                    $enctype_ticket{$1}++;
454                    $enctype_session{$2}++;
455
456                    my $ticket = $1;
457                    my $session = $2;
458
459                    if ($ticket =~ /des-cbc-(crc|md4|md5)/) {
460                              $addr_uses_des{$last_addr}++;
461                              $princ_uses_des{$last_principal}++;
462                    }
463
464          } elsif (/Failed to decrypt PA-DATA -- (.+)$/) {
465                    $pa_failed++;
466                    $pa_failed_princ{$last_principal}++;
467                    $pa_failed_addr{$last_addr}++;
468
469          } elsif (/Request to forward non-forwardable ticket/) {
470                    $forward_non_forward++;
471          } elsif (/HTTP request:/) {
472          } elsif (/krb_rd_req: Incorrect network address/) {
473          } elsif (/krb_rd_req: Ticket expired \(krb_rd_req\)/) {
474          } elsif (/Ticket expired \(.*\)/) {
475          } elsif (/krb_rd_req: Can't decode authenticator \(krb_rd_req\)/) {
476          } elsif (/Request from wrong address/) {
477                    # XXX
478          } elsif (/UNKNOWN --/) {
479                    # XXX
480          } elsif (/Too large time skew -- (.*)$/) {
481                    # XXX
482          } elsif (/No PA-ENC-TIMESTAMP --/) {
483                    # XXX
484          } elsif (/Looking for pa-data --/) {
485                    # XXX
486          } elsif (/Pre-authentication succeded -- (.+)$/) {
487                    # XXX
488          } elsif (/Bad request for ([,a-zA-Z0-9]+) ticket/) {
489                    # XXX
490          } elsif (/Failed to verify AP-REQ: Ticket expired/) {
491                    # XXX
492          } elsif (/Client not found in database:/) {
493                    # XXX
494          } elsif (/Server not found in database \(krb4\)/) {
495          } elsif (/Server not found in database:/) {
496                    # XXX
497          } elsif (/newsyslog.*logfile turned over/) {
498                    # Nothing
499          } elsif (/Requested flags:/) {
500                    # Nothing
501          } elsif (/shutting down/) {
502                    # Nothing
503          } elsif (/listening on IP/) {
504                    # Nothing
505          } elsif (/commencing operation/) {
506                    $restarts++;
507          }
508          #
509          # Log it if we didn't parse the line
510          #
511          else {
512                    print "Unknown log file line: $_";
513          }
514}
515
516sub topten {
517          my ($list) = @_;
518          my @keys;
519
520          my $key;
521
522          @keys = (sort {$$list{$b} <=> $$list{$a}} (keys %{$list}));
523          splice @keys, 10;
524
525          foreach $key (@keys) {
526                    print "\t\t$key - $$list{$key}\n";
527          }
528}
529
530sub islocaladdr (\$) {
531          my ($addr) = @_;
532          my $net;
533
534          foreach $net (@local_networks_re) {
535                    return 1 if ($addr =~ /$net/);
536          }
537          return 0;
538}
539
540sub islocalrealm (\$) {
541          my ($princ) = @_;
542          my $realm;
543
544          foreach $realm (@local_realms) {
545                    return 1 if ($princ eq $realm);
546                    return 1 if ($princ =~ /[^@]+\@${realm}/);
547          }
548          return 0;
549}
550