File Coverage

blib/lib/Net/Radius/Server/PAM.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Net::Radius::Server::PAM;
2              
3 1     1   2248 use 5.008;
  1         3  
  1         42  
4 1     1   5 use strict;
  1         2  
  1         77  
5 1     1   7 use warnings;
  1         2  
  1         29  
6 1     1   467 use Authen::PAM;
  0            
  0            
7             use Carp qw/croak/;
8             use Net::Radius::Packet;
9             use base qw/Net::Radius::Server::Match Net::Radius::Server::Set::Simple/;
10             use Net::Radius::Server::Base qw/:all/;
11              
12             our $VERSION = do { sprintf "%0.3f", 1+(q$Revision: 75 $ =~ /\d+/g)[0]/1000 };
13              
14             __PACKAGE__->mk_accessors(qw/service store_result/);
15              
16             sub mk { croak __PACKAGE__ . " factories are ->fmatch() and ->fset()\n" }
17              
18             sub fmatch { Net::Radius::Server::Match::mk(@_); }
19             sub fset { Net::Radius::Server::Set::mk(@_); }
20              
21             sub _delay_dummy { 1; }
22              
23             sub _pam_init
24             {
25             my $self = shift;
26             my $r_data = shift;
27              
28             my $store = '_pamh';
29             $store = $self->store_result if $self->store_result;
30              
31             if ($r_data->{$store})
32             {
33             $self->log(4, "Already authenticated");
34             return PAM_SUCCESS();
35             }
36              
37             my $req = $r_data->{request};
38             my $secret = $r_data->{secret};
39            
40             my $u_attr = 'User-Name';
41             my $p_attr = 'User-Password';
42              
43             # Fail if no user and password
44             return PAM_ABORT() unless $req->attr($u_attr) and $req->attr($p_attr);
45              
46             my $user = $req->attr($u_attr);
47             my $pass = $req->password($secret, $p_attr);
48              
49             my $pamh = new Authen::PAM
50             (
51             ($self->service || 'login'), $user, sub
52             {
53             my @res;
54             while (@_)
55             {
56             my $msg_type = shift;
57             my $msg = shift;
58             $self->log(4, "(_conv_f) $msg_type -> $msg");
59             push @res, (0, $pass);
60             }
61             push @res, PAM_SUCCESS();
62             return @res;
63             },
64             );
65              
66             unless (ref($pamh))
67             {
68             $self->log(2, "Failed to init PAM: $pamh");
69             return PAM_ABORT();
70             }
71              
72             if ($pamh->pam_fail_delay(0) != PAM_SUCCESS()
73             and $pamh->pam_set_item(PAM_FAIL_DELAY(),
74             \&_delay_dummy) != PAM_SUCCESS())
75             {
76             $self->log(2, "Cannot avoid PAM delay on failure");
77             }
78              
79             my $res = $pamh->pam_authenticate(0x0);
80             if ($res == PAM_SUCCESS())
81             {
82             $self->log(4, "Store pamh in $store");
83             $r_data->{$store} = $pamh;
84             $self->log(4, "Authentication succesful");
85             }
86             else
87             {
88             $self->log(2, "Failed to authenticate: $res");
89             }
90              
91             return $res;
92             }
93              
94             sub _match
95             {
96             my $self = shift;
97             my $r_data = shift;
98              
99             if ($self->_pam_init($r_data, @_) == PAM_SUCCESS())
100             {
101             return NRS_MATCH_OK;
102             }
103             else
104             {
105             return NRS_MATCH_FAIL;
106             }
107             }
108              
109             sub _set
110             {
111             my $self = shift;
112             my $r_data = shift;
113              
114             $self->code('Access-Accept') unless $self->code;
115              
116             return NRS_SET_CONTINUE
117             unless $self->_pam_init($r_data, @_) == PAM_SUCCESS();
118              
119             my $store = '_pamh';
120             $store = $self->store_result if $self->store_result;
121              
122             my $pamh = $r_data->{$store};
123             my $req = $r_data->{request};
124             my $res = $r_data->{response};
125              
126             # Convert environment to RADIUS attribues;
127             my %env = $pamh->pam_getenvlist;
128             while (my ($k, $v) = each %env)
129             {
130             next unless defined $r_data->{dict}->attr_num($k);
131             $self->log(4, "Set attr $k => $v");
132             $res->set_attr($k, $v);
133             }
134              
135             $self->SUPER::_set($r_data, @_);
136             }
137              
138             42;
139              
140             __END__