File Coverage

blib/lib/Authen/SASL/Perl.pm
Criterion Covered Total %
statement 76 168 45.2
branch 20 60 33.3
condition 18 38 47.3
subroutine 21 37 56.7
pod 0 19 0.0
total 135 322 41.9


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