| Author: Daniel F. Dickinson <cshored@thecshore.com> |
| Date: Sun Jan 27 01:04:25 2019 -0500 |
| |
| gitolite: Eliminate the need for ssh-keygen dependency |
| |
| Previously gitolite used ssh-keygen to generate fingerprints |
| from OpenSSH keys to ensure non-duplication of keys when |
| processing them to create / manage user ssh access to the |
| git repositories. This ends up depending on openssl, |
| which is large and unnecessary when we are running on an |
| embedded distro such as OpenWrt. |
| |
| Signed-off-by: Daniel F. Dickinson <cshored@thecshore.com> |
| --- a/src/lib/Gitolite/Common.pm |
| +++ b/src/lib/Gitolite/Common.pm |
| @@ -26,6 +26,8 @@ package Gitolite::Common; |
| use Exporter 'import'; |
| use File::Path qw(mkpath); |
| use File::Temp qw(tempfile); |
| +use MIME::Base64 qw(decode_base64); |
| +use Digest::SHA qw(sha256_base64); |
| use Carp qw(carp cluck croak confess); |
| |
| use strict; |
| @@ -352,43 +354,82 @@ sub logger_plus_stderr { |
| } |
| |
| # ---------------------------------------------------------------------- |
| +# Decode OpenSSH key |
| +# If the key cannot be parsed it will be undef |
| +# Returns (algorithm_name, algo_data1, algo_data2, ...) |
| +sub ssh_decode_key($) { |
| + my $key = shift; |
| + my $keydata = decode_base64($key); |
| + my @keyparts = (); |
| + my $partlen; |
| + my $algorithm; |
| + my $data; |
| + my $pos = 0; |
| + $partlen = unpack('N', substr $keydata, $pos, 4) or return undef; |
| + $algorithm = substr $keydata, $pos + 4, $partlen or return undef; |
| + $pos = $pos + 4 + $partlen; |
| + while ( $pos <= length($keydata) ) { |
| + $partlen = unpack('N', substr $keydata, $pos, 4) or last; |
| + $data = unpack('s>*', substr $keydata, $pos + 4, 4) or last; |
| + $pos = $pos + 4 + $partlen; |
| + push @keyparts, $data; |
| + } |
| + return ( $algorithm, @keyparts ); |
| +} |
| + |
| +# ---------------------------------------------------------------------- |
| +# Parse OpenSSH line |
| +# If the file cannot be parsed it will be undef |
| +# Returns (restrictions, algorithm, PEMkey, comment) |
| +sub ssh_parse_line($) { |
| + my $ssh_line = shift; |
| + my @ssh_parts = split / /, $ssh_line, 5; |
| + if (scalar @ssh_parts < 4) { |
| + @ssh_parts = ('', @ssh_parts); |
| + } |
| + if (scalar @ssh_parts > 4) { |
| + @ssh_parts = @ssh_parts[0,3] |
| + } |
| + if (scalar @ssh_parts < 4) { |
| + @ssh_parts = undef; |
| + } |
| + return ( @ssh_parts ); |
| +} |
| + |
| +# ---------------------------------------------------------------------- |
| +# Get the SSH fingerprint of a line of text |
| +# If the fingerprint cannot be parsed, it will be undef |
| +# In a scalar context, returns the fingerprint |
| +# In a list context, returns (fingerprint, output) where output |
| +# is the parsed input line (less algorithm) |
| +sub ssh_fingerprint_line($) { |
| + my $ssh_line = shift; |
| + my @parsed_line = ssh_parse_line($ssh_line) or return undef; |
| + my @ssh_parts = ssh_decode_key($parsed_line[2]) or return undef; |
| + ( $parsed_line[1] eq $ssh_parts[0] ) or die "algorithm mismatch: $parsed_line[1] vs. $ssh_parts[0]"; |
| + my $fp = sha256_base64(join(' ', @ssh_parts[1,-1])); |
| + return wantarray ? ($fp, join(' ', @ssh_parts[1,-1])) : $fp; |
| +} |
| + |
| +# ---------------------------------------------------------------------- |
| # Get the SSH fingerprint of a file |
| # If the fingerprint cannot be parsed, it will be undef |
| # In a scalar context, returns the fingerprint |
| # In a list context, returns (fingerprint, output) where output |
| -# is the raw output of the ssh-keygen command |
| -sub ssh_fingerprint_file { |
| +# is the raw input line |
| +sub ssh_fingerprint_file($) { |
| my $in = shift; |
| -f $in or die "file not found: $in\n"; |
| my $fh; |
| - open( $fh, "ssh-keygen -l -f $in |" ) or die "could not fork: $!\n"; |
| + open( $fh, $in ) or die "could not open $in: $!\n"; |
| my $output = <$fh>; |
| chomp $output; |
| - # dbg("fp = $fp"); |
| close $fh; |
| # Return a valid fingerprint or undef |
| - my $fp = undef; |
| - if($output =~ /((?:MD5:)?(?:[0-9a-f]{2}:){15}[0-9a-f]{2})/i or |
| - $output =~ m{((?:RIPEMD|SHA)\d+:[A-Za-z0-9+/=]+)}i) { |
| - $fp = $1; |
| - } |
| + my $fp = ssh_fingerprint_line($output); |
| return wantarray ? ($fp, $output) : $fp; |
| } |
| |
| -# Get the SSH fingerprint of a line of text |
| -# If the fingerprint cannot be parsed, it will be undef |
| -# In a scalar context, returns the fingerprint |
| -# In a list context, returns (fingerprint, output) where output |
| -# is the raw output of the ssh-keygen command |
| -sub ssh_fingerprint_line { |
| - my ( $fh, $fn ) = tempfile(); |
| - print $fh shift() . "\n"; |
| - close $fh; |
| - my ($fp,$output) = ssh_fingerprint_file($fn); |
| - unlink $fn; |
| - return wantarray ? ($fp,$output) : $fp; |
| -} |
| - |
| # ---------------------------------------------------------------------- |
| |
| # bare-minimum subset of 'Tsh' (see github.com/sitaramc/tsh) |