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