File Coverage

blib/lib/Net/SSH/AuthorizedKeysFile.pm
Criterion Covered Total %
statement 112 146 76.7
branch 32 54 59.2
condition 1 3 33.3
subroutine 17 21 80.9
pod 10 14 71.4
total 172 238 72.2


line stmt bran cond sub pod time code
1             ###########################################
2             package Net::SSH::AuthorizedKeysFile;
3             ###########################################
4 8     8   412123 use strict;
  8         13  
  8         249  
5 8     8   78 use warnings;
  8         13  
  8         248  
6 8     8   2706 use Log::Log4perl qw(:easy);
  8         108846  
  8         39  
7 8     8   6938 use Text::ParseWords;
  8         7647  
  8         402  
8 8     8   2716 use Net::SSH::AuthorizedKey;
  8         13  
  8         194  
9 8     8   37 use Net::SSH::AuthorizedKey::SSH1;
  8         9  
  8         113  
10 8     8   21 use Net::SSH::AuthorizedKey::SSH2;
  8         8  
  8         8695  
11              
12             our $VERSION = "0.18";
13              
14             ###########################################
15             sub new {
16             ###########################################
17 24     24 1 8132 my($class, @options) = @_;
18              
19 24         138 my $self = {
20             default_file => "$ENV{HOME}/.ssh/authorized_keys",
21             strict => 0,
22             abort_on_error => 0,
23             append => 0,
24             ridiculous_line_len => 100_000,
25             @options,
26             };
27              
28 24         36 bless $self, $class;
29              
30             # We allow keys to be set in the constructor
31 24 50       95 my $keys = $self->{keys} if exists $self->{keys};
32              
33 24         55 $self->reset();
34              
35 24 50       47 $self->{keys} = $keys if defined $keys;
36              
37 24         49 return $self;
38             }
39              
40             ###########################################
41             sub sanity_check {
42             ###########################################
43 3     3 1 1553 my($self, $file) = @_;
44              
45 3 100       10 $self->{file} = $file if defined $file;
46 3 50       6 $self->{file} = $self->{default_file} if !defined $self->{file};
47              
48 3         5 my $result = undef;
49              
50 3         3 my $fh;
51              
52 3 50       91 if(! open $fh, "<$self->{file}") {
53 0         0 ERROR "Cannot open file $self->{file}";
54 0         0 return undef;
55             }
56              
57 3         106 while(
58             defined(my $rc =
59             sysread($fh, my $chunk, $self->{ridiculous_line_len}))) {
60 4 100       9 if($rc < $self->{ridiculous_line_len}) {
61 2         2 $result = 1;
62 2         3 last;
63             }
64              
65 2 100       17 if(index( $chunk, "\n" ) >= 0) {
66             # contains a newline, looks good
67 1         5 next;
68             }
69              
70             # we've got a line that's between ridiculous_line_len and
71             # 2*ridiculous_line_len characters long. Pull the plug.
72 1         8 $self->error("File $self->{file} contains insanely long lines " .
73             "(> $self->{ridiculous_line_len} chars");
74 1         1 last;
75             }
76              
77             DONE:
78 3         16 close $fh;
79              
80 3 100       7 if(!$result) {
81 1         5 ERROR "Sanity check of file $self->{file} failed";
82             }
83 3         18 return $result;
84             }
85              
86             ###########################################
87             sub keys {
88             ###########################################
89 16     16 1 56 my($self) = @_;
90              
91 16         15 return @{$self->{keys}};
  16         57  
92             }
93              
94             ###########################################
95             sub reset {
96             ###########################################
97 47     47 0 45 my($self) = @_;
98              
99 47         63 $self->{keys} = [];
100 47         73 $self->{content} = "";
101 47         58 $self->{error} = undef;
102             }
103              
104             ###########################################
105             sub content {
106             ###########################################
107 0     0 1 0 my($self, $new_content) = @_;
108              
109 0 0       0 if( defined $new_content ) {
110 0         0 $self->reset();
111 0         0 $self->{content} = $new_content;
112             }
113              
114 0         0 return $self->{content};
115             }
116              
117             ###########################################
118             sub line_parse {
119             ###########################################
120 57     57 0 63 my($self, $line, $line_number) = @_;
121              
122 57         71 chomp $line;
123              
124 57         138 DEBUG "Parsing line [$line]";
125              
126 57         262 $self->error( "" );
127              
128 57         153 my $pk = Net::SSH::AuthorizedKey->parse( $line );
129              
130 57 100       96 if( !$pk ) {
131 12         17 my $msg = "[$line] rejected by all parsers";
132 12         18 WARN $msg;
133 12         50 $self->error($msg);
134 12         13 return undef;
135             }
136              
137 45 50 33     103 if(! $self->{strict} or $pk->sanity_check()) {
138 45         57 return $pk;
139             }
140              
141 0         0 WARN "Key [$line] failed sanity check";
142              
143 0 0       0 if($self->{strict}) {
144 0         0 $self->error( $pk->error() );
145 0         0 return undef;
146             }
147              
148             # Key is corrupted, but ok in non-strict mode
149 0         0 return $pk;
150             }
151              
152             ###########################################
153             sub parse {
154             ###########################################
155 23     23 0 27 my($self) = @_;
156              
157 23         33 $self->{keys} = [];
158 23         35 $self->{error} = "";
159              
160 23         27 my $line_number = 0;
161              
162 23         87 for my $line (split /\n/, $self->{content}) {
163 66         55 $line_number++;
164              
165 66         125 $line =~ s/^\s+//; # Remove leading blanks
166 66         149 $line =~ s/\s+$//; # Remove trailing blanks
167 66 100       128 next if $line =~ /^$/; # Ignore empty lines
168 62 100       93 next if $line =~ /^#/; # Ignore comment lines
169              
170 57         93 my $key = $self->line_parse($line, $line_number);
171              
172 57 100       84 if( defined $key ) {
173 45         33 push @{$self->{keys}}, $key;
  45         84  
174             } else {
175 12 100       22 if($self->{abort_on_error}) {
176 2         4 $self->error("Line $line_number: " . $self->error());
177 2         4 return undef;
178             }
179             }
180             }
181              
182 21         46 return 1;
183             }
184              
185             ###########################################
186             sub read {
187             ###########################################
188 23     23 1 748 my($self, $file) = @_;
189              
190 23         39 $self->reset();
191              
192 23 50       48 $self->{file} = $file if defined $file;
193 23 50       55 $self->{file} = $self->{default_file} if !defined $self->{file};
194 23         24 $self->{content} = "";
195              
196 23         87 DEBUG "Reading in $self->{file}";
197              
198 23 50       691 open FILE, "<$self->{file}" or LOGDIE "Cannot open $self->{file}";
199              
200 23         296 while() {
201 66         182 $self->{content} .= $_;
202             }
203              
204 23         113 close FILE;
205              
206 23         57 return $self->parse();
207             }
208              
209             ###########################################
210             sub as_string {
211             ###########################################
212 5     5 1 8 my($self) = @_;
213              
214 5         7 my $string = "";
215              
216 5         6 for my $key ( @{ $self->{keys} } ) {
  5         13  
217 14         28 $string .= $key->as_string . "\n";
218             }
219              
220 5         29 return $string;
221             }
222              
223             ###########################################
224             sub save {
225             ###########################################
226 4     4 1 11 my($self, $file) = @_;
227              
228 4 50       11 if(!defined $file) {
229 4         49 $file = $self->{file};
230             }
231              
232 4 50       211 if(! open FILE, ">$file") {
233 0         0 $self->error("Cannot open $file ($!)");
234 0         0 WARN $self->error();
235 0         0 return undef;
236             }
237              
238 4         17 print FILE $self->as_string();
239 4         87 close FILE;
240             }
241              
242             ###########################################
243             sub append {
244             ###########################################
245 0     0 0 0 my($self, $key) = @_;
246              
247 0         0 $self->{append} = 1;
248             }
249              
250             ###########################################
251             sub error {
252             ###########################################
253 76     76 1 429 my($self, $text) = @_;
254              
255              
256 76 100       122 if(defined $text) {
257 72         69 $self->{error} = $text;
258              
259 72 100       107 if(length $text) {
260 15         34 ERROR "$text";
261             }
262             }
263              
264 76         130 return $self->{error};
265             }
266              
267             ###########################################
268             sub ssh_dir {
269             ###########################################
270 0     0 1   my($self, $user) = @_;
271              
272 0 0         if(!defined $user) {
273 0           my $uid = $>;
274 0           $user = getpwuid($uid);
275 0 0         if(!defined $user) {
276 0           ERROR "getpwuid of $uid failed ($!)";
277 0           return undef;
278             }
279             }
280              
281 0           my @pwent = getpwnam($user);
282              
283 0 0         if(! defined $pwent[0]) {
284 0           ERROR "getpwnam of $user failed ($!)";
285 0           return undef;
286             }
287              
288 0           my $home = $pwent[7];
289              
290 0           return File::Spec->catfile($home, ".ssh");
291             }
292              
293             ###########################################
294             sub path_locate {
295             ###########################################
296 0     0 1   my($self, $user) = @_;
297              
298 0           my $ssh_dir = $self->ssh_dir($user);
299              
300 0 0         return undef if !defined $ssh_dir;
301              
302 0           return File::Spec->catfile($ssh_dir, "authorized_keys");
303             }
304              
305             1;
306              
307             __END__