Skip Menu |

This queue is for tickets about the RADIUS CPAN distribution.

Report information
The Basics
Id: 75864
Status: new
Priority: 0/
Queue: RADIUS

People
Owner: Nobody in particular
Requestors: cavac [...] cavac.at
Cc:
AdminCc:

Bug Information
Severity: Wishlist
Broken in: 1.0
Fixed in: (no value)



Subject: Add this Net::Server/OATH example
I have attached a little example on how to use Net::Server and and OATH- calculations (Time based one time keys) to implement a RADIUS server. Works fine with OpenSSH. Tested with "OTP c200" one time keys. Using this means to have to change %seeds to reflect your own keys (i replaced the valies with random numbers before uploading).
Subject: netserverexample.pl
#!/usr/bin/perl -w #---AUTOPRAGMASTART--- use 5.012; use strict; use warnings; use diagnostics; use mro 'c3'; use English qw( -no_match_vars ); use Carp; our $VERSION = 0.996; #---AUTOPRAGMAEND--- BEGIN { unshift @INC, "."; }; my $server = Rader->new( port => 1812, proto => "udp", ); print "Server ready to run()...\n"; $server->run(); package Rader; #---AUTOPRAGMASTART--- use 5.012; use strict; use warnings; use diagnostics; use mro 'c3'; use English qw( -no_match_vars ); use Carp; our $VERSION = 0.996; #---AUTOPRAGMAEND--- use base qw(Net::Server::Single); use RADIUS::Dictionary; use RADIUS::Packet; sub process_request { my $self = shift; my $prop = $self->{'server'}; # This is a VERY simple RADIUS authentication server which responds # to Access-Request packets with Access-Accept. This allows anyone # to log in. my $secret = "mysecret"; # Shared secret on the term server # Parse the RADIUS dictionary file (must have dictionary in current dir) my $dict = new RADIUS::Dictionary "dictionary" or die "Couldn't read dictionary: $!"; my $um = OATHusers->new(); # Get the data my $rec = $prop->{udp_data}; # Unpack it my $p = RADIUS::Packet->new($dict, $rec); if ($p->code eq 'Access-Request') { # Print some details about the incoming request (try ->dump here) #print $p->attr('User-Name'), " logging in with password ", # $p->password($secret), "\n"; #$p->dump; # Create a response packet my $rp = new RADIUS::Packet $dict; if($um->validate($p->attr('User-Name'), $p->password($secret))) { $rp->set_code('Access-Accept'); print "Password OK\n"; } else { $rp->set_code('Access-Reject'); print "Password FAIL\n"; } $rp->set_identifier($p->identifier); $rp->set_authenticator($p->authenticator); # (No attributes are needed.. but you could set IP addr, etc. here) # Authenticate with the secret and send to the server. my $outpacket = auth_resp($rp->pack, $secret); $prop->{'client'}->send($outpacket, 0); #$s->sendto(auth_resp($rp->pack, $secret), $whence); } else { # It's not an Access-Request print "***** Unexpected packet type recieved. ******"; $p->dump; } } package OATHusers; use strict; use warnings; use Authen::OATH; sub new { my $class = shift; my $self = bless {}, $class; my %seeds = ( 'user1' => { key_id => '23874987234871234', pin => '1234', seed => uc('23d4cd23c4dc23d4cd23c4d2d34cd23c4dcd234cd'), }, 'user2' => { key_id => '23452345234523455', pin => '74747474', seed => uc('eeeeeeeeeeeeeeeeeeeeeeeeeeeeee5555555555'), }, ); $self->{seeds} = \%seeds; return $self; } sub validate { my ($self, $username, $password) = @_; # Missing fields if(!defined($username) || !defined($password) || $username eq '' || $password eq '') { return 0; } # Unknown username if(!defined($self->{seeds}->{$username})) { return 0; } # For the easy part: Check length of password if(length($password) != (length($self->{seeds}->{$username}->{pin}) + 6)) { return 0; } my $oath = Authen::OATH->new(timestep => 60); my $valid = 0; my $now = time; my $userseed = $self->{seeds}->{$username}->{seed}; for(my $i = -300; $i <= 300; $i += 60) { my $totp = $oath->totp($userseed, $now + $i); my $fullpass = $self->{seeds}->{$username}->{pin} . $totp; if($fullpass eq $password) { print "$username key drift $i\n"; $valid = 1; last; } } return $valid; }