File Coverage

blib/lib/Net/SSH/AuthorizedKeysFile.pm
Criterion Covered Total %
statement 111 145 76.5
branch 30 52 57.6
condition 1 3 33.3
subroutine 17 21 80.9
pod 10 14 71.4
total 169 235 71.9


line stmt bran cond sub pod time code
1             ###########################################
2             package Net::SSH::AuthorizedKeysFile;
3             ###########################################
4 7     7   453285 use strict;
  7         12  
  7         281  
5 7     7   34 use warnings;
  7         8  
  7         222  
6 7     7   3382 use Log::Log4perl qw(:easy);
  7         175246  
  7         58  
7 7     7   8245 use Text::ParseWords;
  7         8779  
  7         447  
8 7     7   3019 use Net::SSH::AuthorizedKey;
  7         20  
  7         218  
9 7     7   41 use Net::SSH::AuthorizedKey::SSH1;
  7         10  
  7         123  
10 7     7   30 use Net::SSH::AuthorizedKey::SSH2;
  7         9  
  7         10269  
11              
12             our $VERSION = "0.17";
13              
14             ###########################################
15             sub new {
16             ###########################################
17 23     23 1 5283 my($class, @options) = @_;
18              
19 23         171 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 23         62 bless $self, $class;
29              
30             # We allow keys to be set in the constructor
31 23 50       115 my $keys = $self->{keys} if exists $self->{keys};
32              
33 23         65 $self->reset();
34              
35 23 50       55 $self->{keys} = $keys if defined $keys;
36              
37 23         59 return $self;
38             }
39              
40             ###########################################
41             sub sanity_check {
42             ###########################################
43 3     3 1 15801 my($self, $file) = @_;
44              
45 3 100       28 $self->{file} = $file if defined $file;
46 3 50       12 $self->{file} = $self->{default_file} if !defined $self->{file};
47              
48 3         7 my $result = undef;
49              
50 3         4 my $fh;
51              
52 3 50       148 if(! open $fh, "<$self->{file}") {
53 0         0 ERROR "Cannot open file $self->{file}";
54 0         0 return undef;
55             }
56              
57 3         145 while(
58             defined(my $rc =
59             sysread($fh, my $chunk, $self->{ridiculous_line_len}))) {
60 4 100       15 if($rc < $self->{ridiculous_line_len}) {
61 2         5 $result = 1;
62 2         4 last;
63             }
64              
65 2 100       182 if(index( $chunk, "\n" ) >= 0) {
66             # contains a newline, looks good
67 1         11 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         12 $self->error("File $self->{file} contains insanely long lines " .
73             "(> $self->{ridiculous_line_len} chars");
74 1         2 last;
75             }
76              
77             DONE:
78 3         22 close $fh;
79              
80 3 100       7 if(!$result) {
81 1         5 ERROR "Sanity check of file $self->{file} failed";
82             }
83 3         28 return $result;
84             }
85              
86             ###########################################
87             sub keys {
88             ###########################################
89 16     16 1 98 my($self) = @_;
90              
91 16         22 return @{$self->{keys}};
  16         75  
92             }
93              
94             ###########################################
95             sub reset {
96             ###########################################
97 45     45 0 46 my($self) = @_;
98              
99 45         77 $self->{keys} = [];
100 45         85 $self->{content} = "";
101 45         70 $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 55     55 0 80 my($self, $line, $line_number) = @_;
121              
122 55         94 chomp $line;
123              
124 55         172 DEBUG "Parsing line [$line]";
125              
126 55         341 $self->error( "" );
127              
128 55         195 my $pk = Net::SSH::AuthorizedKey->parse( $line );
129              
130 55 100       110 if( !$pk ) {
131 12         18 my $msg = "[$line] rejected by all parsers";
132 12         30 WARN $msg;
133 12         56 $self->error($msg);
134 12         18 return undef;
135             }
136              
137 43 50 33     112 if(! $self->{strict} or $pk->sanity_check()) {
138 43         70 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 22     22 0 33 my($self) = @_;
156              
157 22         45 $self->{keys} = [];
158 22         44 $self->{error} = "";
159              
160 22         29 my $line_number = 0;
161              
162 22         97 for my $line (split /\n/, $self->{content}) {
163 63         64 $line_number++;
164              
165 63         161 $line =~ s/^\s+//; # Remove leading blanks
166 63         176 $line =~ s/\s+$//; # Remove trailing blanks
167 63 100       155 next if $line =~ /^$/; # Ignore empty lines
168 60 100       123 next if $line =~ /^#/; # Ignore comment lines
169              
170 55         123 my $key = $self->line_parse($line, $line_number);
171              
172 55 100       92 if( defined $key ) {
173 43         44 push @{$self->{keys}}, $key;
  43         152  
174             } else {
175 12 100       32 if($self->{abort_on_error}) {
176 2         6 $self->error("Line $line_number: " . $self->error());
177 2         4 return undef;
178             }
179             }
180             }
181              
182 20         61 return 1;
183             }
184              
185             ###########################################
186             sub read {
187             ###########################################
188 22     22 1 175 my($self, $file) = @_;
189              
190 22         38 $self->reset();
191              
192 22 50       48 $self->{file} = $file if defined $file;
193 22 50       59 $self->{file} = $self->{default_file} if !defined $self->{file};
194 22         35 $self->{content} = "";
195              
196 22         105 DEBUG "Reading in $self->{file}";
197              
198 22 50       821 open FILE, "<$self->{file}" or LOGDIE "Cannot open $self->{file}";
199              
200 22         365 while() {
201 63         204 $self->{content} .= $_;
202             }
203              
204 22         125 close FILE;
205              
206 22         68 return $self->parse();
207             }
208              
209             ###########################################
210             sub as_string {
211             ###########################################
212 5     5 1 8 my($self) = @_;
213              
214 5         8 my $string = "";
215              
216 5         8 for my $key ( @{ $self->{keys} } ) {
  5         16  
217 14         38 $string .= $key->as_string . "\n";
218             }
219              
220 5         38 return $string;
221             }
222              
223             ###########################################
224             sub save {
225             ###########################################
226 4     4 1 15 my($self, $file) = @_;
227              
228 4 50       14 if(!defined $file) {
229 4         9 $file = $self->{file};
230             }
231              
232 4 50       303 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         19 print FILE $self->as_string();
239 4         120 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 74     74 1 747 my($self, $text) = @_;
254              
255 74 100       134 if(defined $text) {
256 70         86 $self->{error} = $text;
257 70         208 ERROR "$text";
258             }
259              
260 74         399 return $self->{error};
261             }
262              
263             ###########################################
264             sub ssh_dir {
265             ###########################################
266 0     0 1   my($self, $user) = @_;
267              
268 0 0         if(!defined $user) {
269 0           my $uid = $>;
270 0           $user = getpwuid($uid);
271 0 0         if(!defined $user) {
272 0           ERROR "getpwuid of $uid failed ($!)";
273 0           return undef;
274             }
275             }
276              
277 0           my @pwent = getpwnam($user);
278              
279 0 0         if(! defined $pwent[0]) {
280 0           ERROR "getpwnam of $user failed ($!)";
281 0           return undef;
282             }
283              
284 0           my $home = $pwent[7];
285              
286 0           return File::Spec->catfile($home, ".ssh");
287             }
288              
289             ###########################################
290             sub path_locate {
291             ###########################################
292 0     0 1   my($self, $user) = @_;
293              
294 0           my $ssh_dir = $self->ssh_dir($user);
295              
296 0 0         return undef if !defined $ssh_dir;
297              
298 0           return File::Spec->catfile($ssh_dir, "authorized_keys");
299             }
300              
301             1;
302              
303             __END__