File Coverage

blib/lib/Apache2/AuthenRadius.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 Apache2::AuthenRadius;
2              
3             # $Id: AuthenRadius.pm,v 1.2 2005/04/18 19:29:04 jad Exp $
4              
5 1     1   2439 use strict;
  1         2  
  1         569  
6 1     1   7 use warnings;
  1         2  
  1         41  
7 1     1   6 use vars qw($VERSION);
  1         15  
  1         91  
8 1     1   6163 use Apache2::Const qw(OK HTTP_UNAUTHORIZED DECLINED HTTP_INTERNAL_SERVER_ERROR);
  0            
  0            
9             use Apache2::Connection;
10             use Apache2::RequestRec;
11             use Apache2::Access;
12             use Apache2::RequestUtil;
13             use Apache2::Log;
14             use APR::SockAddr;
15             use Authen::Radius;
16              
17             $VERSION = '0.9';
18              
19             # Create my own method to check a password
20             # The Authen::Radius->check_pwd method was too restrictive
21             # to use. We needed a function that returned all possible
22             # values.
23             sub chk_passwd {
24             my ($rad, $uname, $upwd, $nas) = @_;
25              
26             $rad->clear_attributes;
27             $rad->add_attributes (
28             {Name => 1, Value => $uname, Type => 'string' },
29             {Name => 2, Value => $upwd, Type => 'string' },
30             {Name => 4, Value => $nas, Type => 'ipaddr' }
31             );
32              
33             $rad->send_packet(ACCESS_REQUEST);
34             my $rcv = $rad->recv_packet();
35              
36             return($rcv);
37             }
38              
39              
40             sub handler {
41             my $r = shift;
42            
43             # Continue only if the first request.
44             return OK unless $r->is_initial_req();
45            
46             my $reqs_arr = $r->requires;
47             return OK unless $reqs_arr;
48            
49             # Grab the password, or return if HTTP_UNAUTHORIZED
50             my($res,$pass) = $r->get_basic_auth_pw;
51             return $res if $res;
52            
53             # Get the user name.
54             my $user = $r->user;
55            
56             # Primary Radius Server and port.
57             my $host1 = $r->dir_config("Auth_Radius_host1") or return DECLINED;
58             my $port1 = $r->dir_config("Auth_Radius_port1") || 1647;
59            
60             # Shared secret for the primary host we are running on.
61             my $secret1 = $r->dir_config("Auth_Radius_secret1") or return DECLINED;
62            
63             # Secondary Radius Server and port.
64             my $host2 = $r->dir_config("Auth_Radius_host2");
65             my $port2 = $r->dir_config("Auth_Radius_port2") || 1647;
66            
67             # Shared secret for the secondary host we are running on.
68             my $secret2 = $r->dir_config("Auth_Radius_secret2");
69            
70             # Timeout to wait for a response from the radius server.
71             my $timeout = $r->dir_config("Auth_Radius_timeout") || 5;
72            
73             # Sanity for usernames and passwords.
74             if (length $user > 64 or $user =~ /[^A-Za-z0-9\@\.\-\_\#\:]/) {
75             $r->log_reason("Apache2::AuthenRadius username too long or"
76             ."contains illegal characters. URI:", $r->uri);
77             $r->note_basic_auth_failure;
78             return HTTP_UNAUTHORIZED;
79             }
80              
81             # Prepend realm if set
82             if ($r->dir_config("Auth_Radius_prependToUsername")) {
83             $user = $r->dir_config("Auth_Radius_prependToUsername") . $user;
84             }
85              
86             # Postfix realm if set
87             if ($r->dir_config("Auth_Radius_postfixToUsername")) {
88             $user .= $r->dir_config("Auth_Radius_postfixToUsername");
89             }
90              
91             if (length $pass > 256) {
92             $r->log_reason("Apache2::AuthenRadius password too long. URI:",$r->uri);
93             $r->note_basic_auth_failure;
94             return HTTP_UNAUTHORIZED;
95             }
96            
97             # Create the object for the primary RADIUS query
98             my $radius = Authen::Radius->new(
99             Host => "$host1:$port1",
100             Secret => $secret1,
101             TimeOut => $timeout
102             );
103              
104             # Fail if we can't create object for primary
105             # RADIUS server
106             if (!defined $radius) {
107             $r->log_reason("Apache2::AuthenRadius failed to"
108             ."create object for $host1:$port1. URI:",$r->uri);
109             return HTTP_INTERNAL_SERVER_ERROR;
110             }
111            
112             # Get my IP address to pass as the
113             # NAS IP Address
114             my $c = $r->connection;
115             my $sockaddr = $c->local_addr;
116             my $nas_ip_address = $sockaddr->ip_get;
117            
118             # Check with the primary RADIUS server.
119             my $access = chk_passwd($radius,$user,$pass,$nas_ip_address);
120             if ($access == ACCESS_ACCEPT) {
121             # Good ... we're in
122             return OK;
123             } elsif ($access == ACCESS_REJECT) {
124             # Sorry, you can't get in
125             $r->log_reason("Apache2::AuthenRadius failed for user $user. URI:",
126             $r->uri);
127             $r->note_basic_auth_failure;
128             return HTTP_UNAUTHORIZED;
129             } elsif (!defined($access)) {
130             # We didn't get a response from the primary
131             # server so let's move on to the secondary
132             $r->log_reason("Apache2::AuthenRadius failed to "
133             ."connect to $host1:$port1 will try $host2:$port2. URI:",$r->uri);
134             # Create a new object for the secondary
135             # server
136             my $radius2 = Authen::Radius->new(
137             Host => "$host2:$port2",
138             Secret => $secret2,
139             TimeOut => $timeout
140             );
141             # Fail if we can't create object for secondary
142             # RADIUS server
143             if (!defined $radius2) {
144             $r->log_reason("Apache2::AuthenRadius failed to"
145             ."create object for $host2:$port2. URI:",$r->uri);
146             return HTTP_INTERNAL_SERVER_ERROR;
147             }
148             # Check with the secondary server
149             my $access = chk_passwd($radius2,$user,$pass,$nas_ip_address);
150             if ($access == ACCESS_ACCEPT) {
151             # Good ... we're in
152             return OK;
153             } elsif ($access == ACCESS_REJECT) {
154             # Sorry, you can't get in
155             $r->log_reason("Apache2::AuthenRadius failed for user $user. URI:",
156             $r->uri);
157             $r->note_basic_auth_failure;
158             return HTTP_UNAUTHORIZED;
159             } elsif (!defined($access)) {
160             # We didn't get a response from the secondary
161             # server either
162             $r->log_reason("Apache2::AuthenRadius failed to "
163             ."connect to $host2:$port2. URI:",$r->uri);
164             return HTTP_INTERNAL_SERVER_ERROR;
165             }
166             }
167             }
168              
169             1;
170              
171             __END__