5 # complete rewrite of the sshkeys-lint program. Usage has changed, see
6 # usage() function or run without arguments.
12 GetOptions
( 'admin|a=s' => \
$admin, 'quiet|q' => \
$quiet, 'help|h' => \
$help );
15 $Data::Dumper
::Deepcopy
= 1;
18 my $in_gl_section = 0;
20 my $KEYTYPE_REGEX = qr/\b(?:ssh-(?:rsa|dss|ed25519)|ecdsa-sha2-nistp(?:256|384|521))\b/;
25 print STDERR
"DBG: " . Dumper
($i);
31 return if $quiet and not $warning;
32 $warnings++ if $warning;
33 print "sshkeys-lint: " . ( $warning ?
"WARNING: " : "" ) . $_ for @_;
38 our @pubkeyfiles = @ARGV; @ARGV = ();
39 my $kd = "$ENV{HOME}/.gitolite/keydir";
40 if ( not @pubkeyfiles ) {
41 chomp( @pubkeyfiles = `find $kd -type f -name "*.pub" | sort` );
45 @ARGV = ("$ENV{HOME}/.ssh/authorized_keys");
48 # ------------------------------------------------------------------------
53 msg
0, "==== checking authkeys file:\n";
54 fill_authkeys
(); # uses up STDIN
57 my $fp = fprint
("$admin.pub");
58 my $fpu = ( $fp && $seen_fprints{$fp}{user
} || 'no access' );
59 # dbg("fpu = $fpu, admin=$admin");
61 die "\t\t*** FATAL ***\n" .
62 "$admin.pub maps to $fpu, not $admin.\n" .
63 "You will not be able to access gitolite with this key.\n" .
64 "Look for the 'ssh troubleshooting' link in http://gitolite.com/gitolite/ssh.html.\n"
65 if $fpu ne "user $admin";
69 msg
0, "==== checking pubkeys:\n" if @pubkeyfiles;
70 for my $pkf (@pubkeyfiles) {
71 # get the short name for the pubkey file
72 ( my $pkfsn = $pkf ) =~ s
(^$kd/)();
74 my $fp = fprint
($pkf);
76 msg
1, "$pkfsn appears to be a COPY of $pkf_by_fp{$fp}\n" if $pkf_by_fp{$fp};
77 $pkf_by_fp{$fp} ||= $pkfsn;
78 my $fpu = ( $seen_fprints{$fp}{user
} || 'no access' );
79 msg
0, "$pkfsn maps to $fpu\n";
83 print "\n$warnings warnings found\n";
88 # ------------------------------------------------------------------------
92 next if ak_comment
($_); # also sets/clears $in_gl_section global
96 check
( $seq, $fp, $user );
98 $authkeys[$seq]{fprint
} = $fp;
99 $authkeys[$seq]{ustatus
} = $user;
104 my ( $seq, $fp, $user ) = @_;
106 msg
1, "line $seq, $user key found *outside* gitolite section!\n"
107 if $user =~ /^user / and not $in_gl_section;
109 msg
1, "line $seq, $user key found *inside* gitolite section!\n"
110 if $user !~ /^user / and $in_gl_section;
112 if ( $seen_fprints{$fp} ) {
114 msg
1, "authkeys line $seq ($user) will be ignored by sshd; " .
115 "same key found on line " .
116 $seen_fprints{$fp}{seq
} . " (" .
117 $seen_fprints{$fp}{user
} . ")\n";
122 $seen_fprints{$fp}{seq
} = $seq;
123 $seen_fprints{$fp}{user
} = $user;
128 $user ||= "user $1" if /^command=.*gitolite-shell (.*?)"/;
129 $user ||= "unknown command" if /^command/;
130 $user ||= "shell access" if /$KEYTYPE_REGEX/;
137 $in_gl_section = 1 if /^# gitolite start/;
138 $in_gl_section = 0 if /^# gitolite end/;
139 die "gitosis? what's that?\n" if /^#.*gitosis/;
145 my ( $fh, $tempfn, $in );
146 if ( /$KEYTYPE_REGEX/ ) {
147 # an actual key was passed. Since ssh-keygen requires an actual file,
148 # make a temp file to take the data and pass on to ssh-keygen
149 s/^.* ($KEYTYPE_REGEX)/$1/;
150 use File
::Temp
qw(tempfile);
151 ( $fh, $tempfn ) = tempfile
();
156 # a filename was passed
160 -f
$in or die "file not found: $in\n";
161 open( $fh, "ssh-keygen -l -f $in |" ) or die "could not fork: $!\n";
165 unlink $tempfn if $tempfn;
166 warn "$fp\n" unless $fp =~ /([0-9a-f][0-9a-f](:[0-9a-f][0-9a-f])+)/ or $fp =~ m
(SHA256
:([A
-Za
-z0
-9+/]+));
171 # ------------------------------------------------------------------------
175 Usage: gitolite sshkeys-lint [-q] [optional list of pubkey filenames]
176 (optionally, STDIN can be a pipe or redirected from a file; see below)
178 Look for potential problems in ssh keys.
180 sshkeys-lint expects:
181 - the contents of an authorized_keys file via STDIN, otherwise it uses
182 \$HOME/.ssh/authorized_keys
183 - one or more pubkey filenames as arguments, otherwise it uses all the keys
184 found (recursively) in \$HOME/.gitolite/keydir
186 The '-q' option will print only warnings instead of all mappings.
188 Note that this runs ssh-keygen -l for each line in the authkeys file and each
189 pubkey in the argument list, so be wary of running it on something huge. This
190 is meant for troubleshooting.