File Coverage

blib/lib/Authen/SASL/Perl.pm
Criterion Covered Total %
statement 77 169 45.5
branch 20 60 33.3
condition 17 35 48.5
subroutine 21 37 56.7
pod 0 19 0.0
total 135 320 42.1


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