File Coverage

blib/lib/Authen/SASL/Perl.pm
Criterion Covered Total %
statement 75 166 45.1
branch 20 58 34.4
condition 16 35 45.7
subroutine 20 36 55.5
pod 0 19 0.0
total 131 314 41.7


line stmt bran cond sub pod time code
1             # Copyright (c) 2002 Graham Barr . All rights reserved.
2             # This program is free software; you can redistribute it and/or
3             # modify it under the same terms as Perl itself.
4              
5             package Authen::SASL::Perl;
6              
7 15     15   80 use strict;
  15         30  
  15         1045  
8 15     15   79 use vars qw($VERSION);
  15         24  
  15         635  
9 15     15   73 use Carp;
  15         28  
  15         18919  
10              
11             $VERSION = "2.14";
12              
13             my %secflags = (
14             noplaintext => 1,
15             noanonymous => 1,
16             nodictionary => 1,
17             );
18             my %have;
19              
20             sub server_new {
21 18     18 0 42 my ($pkg, $parent, $service, $host, $options) = @_;
22              
23 18         63 my $self = {
24 18   50     28 callback => { %{$parent->callback} },
      50        
      50        
25             service => $service || '',
26             host => $host || '',
27             debug => $parent->{debug} || 0,
28             need_step => 1,
29             };
30              
31 18 50       72 my $mechanism = $parent->mechanism
32             or croak "No server mechanism specified";
33 18         113 $mechanism =~ s/^\s*\b(.*)\b\s*$/$1/g;
34 18         55 $mechanism =~ s/-/_/g;
35 18         43 $mechanism = uc $mechanism;
36 18         45 my $mpkg = __PACKAGE__ . "::$mechanism";
37 18 50       899 eval "require $mpkg;"
38             or croak "Cannot use $mpkg for " . $parent->mechanism;
39 18         96 my $server = $mpkg->_init($self);
40 18         68 $server->_init_server($options);
41 18         105 return $server;
42             }
43              
44             sub client_new {
45 33     33 0 86 my ($pkg, $parent, $service, $host, $secflags) = @_;
46              
47 33   100     231 my @sec = grep { $secflags{$_} } split /\W+/, lc($secflags || '');
  11         44  
48              
49 33         103 my $self = {
50 33   50     57 callback => { %{$parent->callback} },
      50        
      50        
51             service => $service || '',
52             host => $host || '',
53             debug => $parent->{debug} || 0,
54             need_step => 1,
55             };
56              
57 106         302 my @mpkg = sort {
58 89 50 33     1504 $b->_order <=> $a->_order
      66        
59             } grep {
60 89         269 my $have = $have{$_} ||= (eval "require $_;" and $_->can('_secflags')) ? 1 : -1;
61 89 50       585 $have > 0 and $_->_secflags(@sec) == @sec
62             } map {
63 33 50       160 (my $mpkg = __PACKAGE__ . "::$_") =~ s/-/_/g;
64 89         193 $mpkg;
65             } split /[^-\w]+/, $parent->mechanism
66             or croak "No SASL mechanism found\n";
67              
68 33         238 $mpkg[0]->_init($self);
69             }
70              
71 10     10   13 sub _init_server {}
72              
73 0     0   0 sub _order { 0 }
74 58 100   58 0 779 sub code { defined(shift->{error}) || 0 }
75 68     68 0 6686 sub error { shift->{error} }
76 13     13 0 79 sub service { shift->{service} }
77 21     21 0 147 sub host { shift->{host} }
78              
79             sub need_step {
80 87     87 0 274 my $self = shift;
81 87 100       218 return 0 if $self->{error};
82 83         304 return $self->{need_step};
83             }
84              
85             ## I think I need to rename that to end()?
86             ## It doesn't mean that SASL is successful, but that
87             ## that the negotiation is over, no more step necessary
88             ## at least for the client
89             sub set_success {
90 15     15 0 28 my $self = shift;
91 15         52 $self->{need_step} = 0;
92             }
93              
94             sub is_success {
95 54     54 0 2274 my $self = shift;
96 54   100     156 return !$self->code && !$self->need_step;
97             }
98              
99             sub set_error {
100 30     30 0 49 my $self = shift;
101 30         47 $self->{error} = shift;
102 30         67 return;
103             }
104              
105             # set/get property
106             sub property {
107 190     190 0 246 my $self = shift;
108 190   100     543 my $prop = $self->{property} ||= {};
109 190 100       536 return $prop->{ $_[0] } if @_ == 1;
110 131         288 my %new = @_;
111 131         244 @{$prop}{keys %new} = values %new;
  131         254  
112 131         366 1;
113             }
114              
115             sub callback {
116 36     36 0 46 my $self = shift;
117              
118 36 50       198 return $self->{callback}{$_[0]} if @_ == 1;
119              
120 0         0 my %new = @_;
121 0         0 @{$self->{callback}}{keys %new} = values %new;
  0         0  
122              
123 0         0 $self->{callback};
124             }
125              
126             # Should be defined in the mechanism sub-class
127 0     0 0 0 sub mechanism { undef }
128 3     3 0 15 sub client_step { undef }
129 0     0 0 0 sub client_start { undef }
130 0     0 0 0 sub server_step { undef }
131 0     0 0 0 sub server_start { undef }
132              
133             # Private methods used by Authen::SASL::Perl that
134             # may be overridden in mechanism sub-calsses
135              
136             sub _init {
137 31     31   65 my ($pkg, $href) = @_;
138              
139 31         288 bless $href, $pkg;
140             }
141              
142             sub _call {
143 72     72   161 my ($self, $name) = splice(@_,0,2);
144              
145 72         157 my $cb = $self->{callback}{$name};
146              
147 72 100       224 return undef unless defined $cb;
148              
149 43         48 my $value;
150              
151 43 100       154 if (ref($cb) eq 'ARRAY') {
    100          
152 1         3 my @args = @$cb;
153 1         2 $cb = shift @args;
154 1         4 $value = $cb->($self, @args);
155             }
156             elsif (ref($cb) eq 'CODE') {
157 4         18 $value = $cb->($self, @_);
158             }
159             else {
160 38         54 $value = $cb;
161             }
162              
163 43 100       913 $self->{answer}{$name} = $value
164             unless $name eq 'pass'; # Do not store password
165              
166 43         137 return $value;
167             }
168              
169             # TODO: Need a better name than this
170             sub answer {
171 0     0 0   my ($self, $name) = @_;
172 0           $self->{answer}{$name};
173             }
174              
175 0     0     sub _secflags { 0 }
176              
177             sub securesocket {
178 0     0 0   my $self = shift;
179 0 0 0       return $_[0] unless (defined($self->property('ssf')) && $self->property('ssf') > 0);
180              
181 0           local *GLOB; # avoid used only once warning
182 0           my $glob = \do { local *GLOB; };
  0            
183 0           tie(*$glob, 'Authen::SASL::Perl::Layer', $_[0], $self);
184 0           $glob;
185             }
186              
187             {
188              
189             #
190             # Add SASL encoding/decoding to a filehandle
191             #
192              
193             package Authen::SASL::Perl::Layer;
194              
195 15     15   15158 use bytes;
  15         166  
  15         81  
196              
197             require Tie::Handle;
198             our @ISA = qw(Tie::Handle);
199              
200             sub TIEHANDLE {
201 0     0     my ($class, $fh, $conn) = @_;
202 0           my $self;
203              
204 0 0 0       warn __PACKAGE__ . ': non-blocking handle may not work'
205             if ($fh->can('blocking') and not $fh->blocking());
206              
207 0           $self->{fh} = $fh;
208 0           $self->{conn} = $conn;
209 0           $self->{readbuflen} = 0;
210 0           $self->{sndbufsz} = $conn->property('maxout');
211 0           $self->{rcvbufsz} = $conn->property('maxbuf');
212              
213 0           return bless($self, $class);
214             }
215              
216             sub CLOSE {
217 0     0     my ($self) = @_;
218              
219             # forward close to the inner handle
220 0           close($self->{fh});
221 0           delete $self->{fh};
222             }
223              
224             sub DESTROY {
225 0     0     my ($self) = @_;
226 0           delete $self->{fh};
227 0           undef $self;
228             }
229              
230             sub FETCH {
231 0     0     my ($self) = @_;
232 0           return $self->{fh};
233             }
234              
235             sub FILENO {
236 0     0     my ($self) = @_;
237 0           return fileno($self->{fh});
238             }
239              
240              
241             sub READ {
242 0     0     my ($self, $buf, $len, $offset) = @_;
243 0           my $debug = $self->{conn}->{debug};
244              
245 0           $buf = \$_[1];
246              
247 0           my $avail = $self->{readbuflen};
248              
249 0 0         print STDERR " [READ(len=$len,offset=$offset)] avail=$avail;\n"
250             if ($debug & 4);
251              
252             # Check if there's leftovers from a previous READ
253 0 0         if ($avail <= 0) {
254 0           $avail = $self->_getbuf();
255 0 0         return undef unless ($avail > 0);
256             }
257              
258             # if there's more than we need right now, leave the rest for later
259 0 0         if ($avail >= $len) {
260 0 0         print STDERR " GOT ALL: avail=$avail; need=$len\n"
261             if ($debug & 4);
262 0           substr($$buf, $offset, $len) = substr($self->{readbuf}, 0, $len, '');
263 0           $self->{readbuflen} -= $len;
264 0           return ($len);
265             }
266              
267             # there's not enough; take all we have, read more on next call
268 0 0         print STDERR " GOT PARTIAL: avail=$avail; need=$len\n"
269             if ($debug & 4);
270 0   0       substr($$buf, $offset || 0, $avail) = $self->{readbuf};
271 0           $self->{readbuf} = '';
272 0           $self->{readbuflen} = 0;
273              
274 0           return ($avail);
275             }
276              
277             # retrieve and decode a buffer of cipher text in SASL format
278             sub _getbuf {
279 0     0     my ($self) = @_;
280 0           my $debug = $self->{conn}->{debug};
281 0           my $fh = $self->{fh};
282 0           my $buf = '';
283              
284             # first, read 4-octet buffer size
285 0           my $n = 0;
286 0           while ($n < 4) {
287 0           my $rv = sysread($fh, $buf, 4 - $n, $n);
288 0 0         print STDERR " [getbuf: sysread($fh,$buf,4-$n,$n)=$rv: $!\n"
289             if ($debug & 4);
290 0 0         return $rv unless $rv > 0;
291 0           $n += $rv;
292             }
293              
294             # size is encoded in network byte order
295 0           my ($bsz) = unpack('N', $buf);
296 0 0         print STDERR " [getbuf: cipher buffer sz=$bsz]\n" if ($debug & 4);
297 0 0         return undef unless ($bsz <= $self->{rcvbufsz});
298              
299             # next, read actual cipher text
300 0           $buf = '';
301 0           $n = 0;
302 0           while ($n < $bsz) {
303 0           my $rv = sysread($fh, $buf, $bsz - $n, $n);
304 0 0         print STDERR " [getbuf: got o=$n,n=", $bsz - $n, ",rv=$rv,bl=" . length($buf) . "]\n"
305             if ($debug & 4);
306 0 0         return $rv unless $rv > 0;
307 0           $n += $rv;
308             }
309              
310             # call mechanism specific decoding routine
311 0           $self->{readbuf} = $self->{conn}->decode($buf, $bsz);
312 0           $n = length($self->{readbuf});
313 0 0         print STDERR " [getbuf: clear text buffer sz=$n]\n" if ($debug & 4);
314 0           $self->{readbuflen} = $n;
315             }
316              
317              
318             # Encrypting a write() to a filehandle is much easier than reading, because
319             # all the data to be encrypted is immediately available
320             sub WRITE {
321 0     0     my ($self, undef, $len, $offset) = @_;
322 0           my $debug = $self->{conn}->{debug};
323              
324 0           my $fh = $self->{fh};
325              
326             # put on wire in peer-sized chunks
327 0           my $bsz = $self->{sndbufsz};
328 0           while ($len > 0) {
329 0 0         print STDERR " [WRITE: chunk $bsz/$len]\n"
330             if ($debug & 8);
331              
332             # call mechanism specific encoding routine
333 0   0       my $x = $self->{conn}->encode(substr($_[1], $offset || 0, $bsz));
334 0           print $fh pack('N', length($x)), $x;
335 0           $len -= $bsz;
336 0           $offset += $bsz;
337             }
338              
339 0           return $_[2];
340             }
341              
342             }
343              
344             1;