You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.8 KiB

  1. Author: Daniel F. Dickinson <cshored@thecshore.com>
  2. Date: Sun Jan 27 01:04:25 2019 -0500
  3. gitolite: Eliminate the need for ssh-keygen dependency
  4. Previously gitolite used ssh-keygen to generate fingerprints
  5. from OpenSSH keys to ensure non-duplication of keys when
  6. processing them to create / manage user ssh access to the
  7. git repositories. This ends up depending on openssl,
  8. which is large and unnecessary when we are running on an
  9. embedded distro such as OpenWrt.
  10. Signed-off-by: Daniel F. Dickinson <cshored@thecshore.com>
  11. Index: gitolite-3.6.11/src/lib/Gitolite/Common.pm
  12. ===================================================================
  13. --- gitolite-3.6.11.orig/src/lib/Gitolite/Common.pm
  14. +++ gitolite-3.6.11/src/lib/Gitolite/Common.pm
  15. @@ -26,6 +26,8 @@ package Gitolite::Common;
  16. use Exporter 'import';
  17. use File::Path qw(mkpath);
  18. use File::Temp qw(tempfile);
  19. +use MIME::Base64 qw(decode_base64);
  20. +use Digest::SHA qw(sha256_base64);
  21. use Carp qw(carp cluck croak confess);
  22. use strict;
  23. @@ -352,43 +352,82 @@ sub logger_plus_stderr {
  24. }
  25. # ----------------------------------------------------------------------
  26. +# Decode OpenSSH key
  27. +# If the key cannot be parsed it will be undef
  28. +# Returns (algorithm_name, algo_data1, algo_data2, ...)
  29. +sub ssh_decode_key($) {
  30. + my $key = shift;
  31. + my $keydata = decode_base64($key);
  32. + my @keyparts = ();
  33. + my $partlen;
  34. + my $algorithm;
  35. + my $data;
  36. + my $pos = 0;
  37. + $partlen = unpack('N', substr $keydata, $pos, 4) or return undef;
  38. + $algorithm = substr $keydata, $pos + 4, $partlen or return undef;
  39. + $pos = $pos + 4 + $partlen;
  40. + while ( $pos <= length($keydata) ) {
  41. + $partlen = unpack('N', substr $keydata, $pos, 4) or last;
  42. + $data = unpack('s>*', substr $keydata, $pos + 4, 4) or last;
  43. + $pos = $pos + 4 + $partlen;
  44. + push @keyparts, $data;
  45. + }
  46. + return ( $algorithm, @keyparts );
  47. +}
  48. +
  49. +# ----------------------------------------------------------------------
  50. +# Parse OpenSSH line
  51. +# If the file cannot be parsed it will be undef
  52. +# Returns (restrictions, algorithm, PEMkey, comment)
  53. +sub ssh_parse_line($) {
  54. + my $ssh_line = shift;
  55. + my @ssh_parts = split / /, $ssh_line, 5;
  56. + if (scalar @ssh_parts < 4) {
  57. + @ssh_parts = ('', @ssh_parts);
  58. + }
  59. + if (scalar @ssh_parts > 4) {
  60. + @ssh_parts = @ssh_parts[0,3]
  61. + }
  62. + if (scalar @ssh_parts < 4) {
  63. + @ssh_parts = undef;
  64. + }
  65. + return ( @ssh_parts );
  66. +}
  67. +
  68. +# ----------------------------------------------------------------------
  69. +# Get the SSH fingerprint of a line of text
  70. +# If the fingerprint cannot be parsed, it will be undef
  71. +# In a scalar context, returns the fingerprint
  72. +# In a list context, returns (fingerprint, output) where output
  73. +# is the parsed input line (less algorithm)
  74. +sub ssh_fingerprint_line($) {
  75. + my $ssh_line = shift;
  76. + my @parsed_line = ssh_parse_line($ssh_line) or return undef;
  77. + my @ssh_parts = ssh_decode_key($parsed_line[2]) or return undef;
  78. + ( $parsed_line[1] eq $ssh_parts[0] ) or die "algorithm mismatch: $parsed_line[1] vs. $ssh_parts[0]";
  79. + my $fp = sha256_base64(join(' ', @ssh_parts[1,-1]));
  80. + return wantarray ? ($fp, join(' ', @ssh_parts[1,-1])) : $fp;
  81. +}
  82. +
  83. +# ----------------------------------------------------------------------
  84. # Get the SSH fingerprint of a file
  85. # If the fingerprint cannot be parsed, it will be undef
  86. # In a scalar context, returns the fingerprint
  87. # In a list context, returns (fingerprint, output) where output
  88. -# is the raw output of the ssh-keygen command
  89. -sub ssh_fingerprint_file {
  90. +# is the raw input line
  91. +sub ssh_fingerprint_file($) {
  92. my $in = shift;
  93. -f $in or die "file not found: $in\n";
  94. my $fh;
  95. - open( $fh, "ssh-keygen -l -f $in |" ) or die "could not fork: $!\n";
  96. + open( $fh, $in ) or die "could not open $in: $!\n";
  97. my $output = <$fh>;
  98. chomp $output;
  99. - # dbg("fp = $fp");
  100. close $fh;
  101. # Return a valid fingerprint or undef
  102. - my $fp = undef;
  103. - if($output =~ /((?:MD5:)?(?:[0-9a-f]{2}:){15}[0-9a-f]{2})/i or
  104. - $output =~ m{((?:RIPEMD|SHA)\d+:[A-Za-z0-9+/=]+)}i) {
  105. - $fp = $1;
  106. - }
  107. + my $fp = ssh_fingerprint_line($output);
  108. return wantarray ? ($fp, $output) : $fp;
  109. }
  110. -# Get the SSH fingerprint of a line of text
  111. -# If the fingerprint cannot be parsed, it will be undef
  112. -# In a scalar context, returns the fingerprint
  113. -# In a list context, returns (fingerprint, output) where output
  114. -# is the raw output of the ssh-keygen command
  115. -sub ssh_fingerprint_line {
  116. - my ( $fh, $fn ) = tempfile();
  117. - print $fh shift() . "\n";
  118. - close $fh;
  119. - my ($fp,$output) = ssh_fingerprint_file($fn);
  120. - unlink $fn;
  121. - return wantarray ? ($fp,$output) : $fp;
  122. -}
  123. -
  124. # ----------------------------------------------------------------------
  125. # bare-minimum subset of 'Tsh' (see github.com/sitaramc/tsh)