File Coverage

blib/lib/RADIUS/Packet.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package RADIUS::Packet;
2              
3 1     1   601 use strict;
  1         2  
  1         42  
4             require Exporter;
5 1     1   5 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  1         1  
  1         85  
6             @ISA = qw(Exporter);
7             @EXPORT = qw(auth_resp);
8             @EXPORT_OK = qw( );
9              
10             $VERSION = '1.0';
11              
12 1     1   4 use RADIUS::Dictionary;
  1         1  
  1         15  
13 1     1   879 use Socket;
  1         3968  
  1         599  
14 1     1   1410 use MD5;
  0            
  0            
15              
16             sub new {
17             my ($class, $dict, $data) = @_;
18             my $self = { };
19             bless $self, $class;
20             $self->set_dict($dict) if defined($dict);
21             $self->unpack($data) if defined($data);
22             return $self;
23             }
24              
25             # Set the dictionary
26             sub set_dict {
27             my ($self, $dict) = @_;
28             $self->{Dict} = $dict;
29             }
30              
31             # Functions for accessing data structures
32             sub code { $_[0]->{Code}; }
33             sub identifier { $_[0]->{Identifier}; }
34             sub authenticator { $_[0]->{Authenticator}; }
35              
36             sub set_code { $_[0]->{Code} = $_[1]; }
37             sub set_identifier { $_[0]->{Identifier} = $_[1]; }
38             sub set_authenticator { $_[0]->{Authenticator} = $_[1]; }
39              
40             sub attributes { keys %{$_[0]->{Attributes}}; }
41             sub attr { $_[0]->{Attributes}->{$_[1]}; }
42             sub set_attr { $_[0]->{Attributes}->{$_[1]} = $_[2]; }
43              
44             # Decode the password
45             sub password {
46             my ($self, $secret) = @_;
47             my $lastround = $self->authenticator;
48             my $pwdin = $self->attr("Password");
49             my $pwdout;
50             for (my $i = 0; $i < length($pwdin); $i += 16) {
51             $pwdout .= substr($pwdin, $i, 16) ^ MD5->hash($secret . $lastround);
52             $lastround = substr($pwdin, $i, 16);
53             }
54             $pwdout =~ s/\000*$//;
55             return $pwdout;
56             }
57              
58             # Set response authenticator in binary packet
59             sub auth_resp {
60             my $new = $_[0];
61             substr($new, 4, 16) = MD5->hash($_[0] . $_[1]);
62             return $new;
63             }
64              
65             # Utility functions for printing/debugging
66             sub pdef { defined $_[0] ? $_[0] : "UNDEF"; }
67             sub pclean {
68             my $str = $_[0];
69             $str =~ s/([\000-\037\177-\377])/<${\ord($1)}>/g;
70             return $str;
71             }
72              
73             sub dump {
74             my $self = shift;
75             print "*** DUMP OF RADIUS PACKET ($self)\n";
76             print "Code: ", pdef($self->{Code}), "\n";
77             print "Identifier: ", pdef($self->{Identifier}), "\n";
78             print "Authentic: ", pclean(pdef($self->{Authenticator})), "\n";
79             print "Attributes:\n";
80             foreach my $attr ($self->attributes) {
81             printf " %-20s %s\n", $attr . ":" , pclean($self->attr($attr));
82             }
83             print "*** END DUMP\n";
84              
85             }
86              
87             sub pack {
88             my $self = shift;
89             my $hdrlen = 1 + 1 + 2 + 16; # Size of packet header
90             my $p_hdr = "C C n a16 a*"; # Pack template for header
91             my $p_attr = "C C a*"; # Pack template for attribute
92             my %codes = ('Access-Request' => 1, 'Access-Accept' => 2,
93             'Access-Reject' => 3, 'Accounting-Request' => 4,
94             'Accounting-Response' => 5, 'Access-Challenge' => 11,
95             'Status-Server' => 12, 'Status-Client' => 13);
96             my $attstr = ""; # To hold attribute structure
97             # Define a hash of subroutine references to pack the various data types
98             my %packer = ("string" => sub {
99             return $_[0];
100             },
101             "integer" => sub {
102             # return pack "N", $self->{Dict}->{val}->{$_[1]} ?
103             return pack "N", $self->{Dict}->attr_has_val($_[1]) ?
104             $self->{Dict}->val_num(@_[1, 0]) : $_[0];
105             },
106             "ipaddr" => sub {
107             return inet_aton($_[0]);
108             },
109             "time" => sub {
110             return pack "N", $_[0];
111             });
112              
113             # Pack the attributes
114             foreach my $attr ($self->attributes) {
115             my $val = &{$packer{$self->{Dict}->attr_type($attr)}}($self->attr($attr),
116             $self->{Dict}->attr_num($attr));
117             $attstr .= pack $p_attr, $self->{Dict}->attr_num($attr), length($val)+2, $val;
118             }
119             # Prepend the header and return the complete binary packet
120             return pack $p_hdr, $codes{$self->code}, $self->identifier,
121             length($attstr) + $hdrlen, $self->authenticator,
122             $attstr;
123             }
124              
125             sub unpack {
126             my ($self, $data) = @_;
127             my $dict = $self->{Dict};
128             my $p_hdr = "C C n a16 a*"; # Pack template for header
129             my $p_attr = "C C a*"; # Pack template for attribute
130             my %rcodes = (1 => 'Access-Request', 2 => 'Access-Accept',
131             3 => 'Access-Reject', 4 => 'Accounting-Request',
132             5 => 'Accounting-Response', 11 => 'Access-Challenge',
133             12 => 'Status-Server', 13 => 'Status-Client');
134              
135             # Decode the header
136             my ($code, $id, $len, $auth, $attrdat) = unpack $p_hdr, $data;
137              
138             # Generate a skeleton data structure to be filled in
139             $self->set_code($rcodes{$code});
140             $self->set_identifier($id);
141             $self->set_authenticator($auth);
142              
143             # Functions for the various data types
144             my %unpacker = ("string" => sub {
145             return $_[0];
146             },
147             "integer" => sub {
148             return $dict->val_has_name($_[1]) ?
149             $dict->val_name($_[1], unpack("N", $_[0]))
150             : unpack("N", $_[0]);
151             },
152             "ipaddr" => sub {
153             return inet_ntoa($_[0]);
154             },
155             "time" => sub {
156             return unpack "N", $_[0];
157             });
158              
159             # Unpack the attributes
160             while (length($attrdat)) {
161             my $length = unpack "x C", $attrdat;
162             my ($type, $value) = unpack "C x a${\($length-2)}", $attrdat;
163             my $val = &{$unpacker{$dict->attr_numtype($type)}}($value, $type);
164             $self->set_attr($dict->attr_name($type), $val);
165             substr($attrdat, 0, $length) = "";
166             }
167             }
168              
169             1;
170             __END__