1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
#!/usr/bin/perl -w
#
# used-principals -- Report which Kerberos v5 principals are in use.
#
# Written by Russ Allbery <rra@stanford.edu>
# Copyright 2008
# The Board of Trustees of the Leland Stanford Junior University
#
# See LICENSE for licensing terms.
require 5.006;
use strict;
use Getopt::Long qw(GetOptions);
# When converting from Kerberos v4 principal names to Kerberos v5, the
# following types will be taken as host-based and will have the domain name
# appended.
our %HOST_BASED = map { $_ => 1 }
qw(HTTP afpserver cifs ftp host ident imap ldap nfs pop sieve smtp
uniengd webauth);
# Parse command-line options.
my ($count, $help, $k4, $principals);
Getopt::Long::config ('no_ignore_case', 'bundling');
GetOptions ('c|count' => \$count,
'h|help' => \$help,
'k|k4|kerberos4=s' => \$k4,
'p|principals=s' => \$principals) or exit 1;
if ($help) {
print "Feeding myself to perldoc, please wait....\n";
exec ('perldoc', '-t', $0);
}
my @logs = @ARGV;
# Clean up $0 for error reporting.
$0 =~ s%.*/%%;
# If we were told to read a list of interesting principals from a file, do
# that now. Transform them into Kerberos v5 principals if -4 was given.
my %wanted;
if ($principals) {
open (PRINCS, '<', $principals)
or die "$0: cannot open $principals: $!\n";
while (<PRINCS>) {
s/^\s+//;
next if /^\s+$/;
s/\s+$//;
next if /^\#/;
if ($k4) {
my ($type, $instance) = split (/\./, $_, 2);
$type = 'host' if $type eq 'rcmd';
if ($HOST_BASED{$type}) {
$instance .= '.' . $k4;
}
$_ = "$type/$instance";
}
$wanted{$_} = 1;
}
close PRINCS;
}
# Now, scan the logs looking for successful ticket requests. Count both the
# client principal and the service principal as used. Logs may either be
# regular file names, files ending in .gz, or files ending in .bz2.
my %seen;
if (!@logs) {
push (@logs, '-');
}
for my $log (@logs) {
if ($log =~ /\.gz\z/) {
open (LOG, '-|', 'gzip', '-dc', $log)
or die "$0: cannot run gzip for $log: $!\n";
} elsif ($log =~ /\.bz2\z/) {
open (LOG, '-|', 'bzip2', '-dc', $log)
or die "$0: cannot run bzip2 for $log: $!\n";
} elsif ($log eq '-') {
open (LOG, '<&', \*STDIN)
or die "$0: cannot dup standard input: $!\n";
} else {
open (LOG, '<', $log) or die "$0: cannot open $log: $!\n";
}
while (<LOG>) {
if (/\b(?:AS|TGS)_REQ .* ISSUE: .* (\S+)\@\S+ for (\S+)\@\S+$/) {
my ($client, $server) = ($1, $2);
$seen{$client}++ if (!$principals || $wanted{$client});
$seen{$server}++ if (!$principals || $wanted{$server});
}
}
close LOG;
}
# Print out all the principals that we've seen and, if -c was given, the
# number of times we saw that principal.
for my $principal (sort keys %seen) {
if ($count) {
print "$principal $seen{$principal}\n";
} else {
print "$principal\n";
}
}
exit 0;
__END__
##############################################################################
# Documentation
##############################################################################
=for stopwords
KDC bzip2
=head1 NAME
used-principals - Report which Kerberos v5 principals are in use
=head1 SYNOPSIS
B<used-principals> [B<-ch>] [B<-p> I<list> [B<-k> I<domain>]] [I<log> ...]
=head1 DESCRIPTION
B<used-principals> scans an MIT Kerberos KDC log and reports on which
principals were used successfully. "Used" for this program means that the
principal either successfully requested a ticket or a service ticket was
successfully requested for that principal. The provided log files may be
regular files, files ending in C<.gz> or C<.bz2> (which will be
uncompressed with B<gzip> or B<bzip2>), or C<-> (indicating standard
input). If no log files are given on the command line, log entries will
be read from standard input.
All principals seen as active in the logs will be printed to standard
output, one per line, unless the B<-p> option was given. If B<-p> was
given, the logs will be scanned only for the principals listed in the file
given as an argument to B<-p>, and only principals from that file seen in
the logs will be printed. This can be used to find which principals in a
given set are active.
=head1 OPTIONS
=over 4
=item B<-c>, B<--count>
Instead of printing only an active principal, print the principal, a
space, and the number of times that principal was seen in the logs (as
either obtaining a ticket or having a ticket obtained for it).
=item B<-h>, B<--help>
Print out this documentation (which is done simply by feeding the script
to C<perldoc -t>).
=item B<-k> I<domain>, B<--k4>=I<domain>, B<--kerberos4>=I<domain>
Meaningful only when used with the B<-p> option, this option says to
interpret the principals listed in that file as Kerberos v4 principal
names instead of Kerberos v5 principal names. They will be converted to
the corresponding Kerberos v5 principals before scanning the logs.
I<domain> is the local domain to append to host-based Kerberos v4
principals (such as C<rcmd.system>, which becomes
C<host/system.I<domain>>).
=item B<-p> I<list>, B<--principals>=I<list>
Scan only for the principals listed in the file I<list> and only report on
principals found in that set.
=back
=head1 CAVEATS
The B<-4> option was implement for reporting around a specific transition
at Stanford University and uses a hard-coded list of Kerberos v4
principals that should be considered host-based. It also makes other
assumptions that could be specific for that one use. Using it for other
purposes may require some tweaking.
=head1 AUTHOR
Russ Allbery <rra@stanford.edu>
=cut
|