#!/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--- use Authen::Radius; use Term::ReadKey; my $username = 'cavac'; my $password; unless (defined $password) { print 'Password: '; ReadMode('noecho'); chomp($password = ReadLine(0)); ReadMode('restore'); print "\n"; } my $r = new Authen::Radius(Host => '127.0.0.1', Secret => 'mysecret', TimeOut=>10); if($r->check_pwd($username, $password)) { print "Welcome!\n"; } else { print "***** FAIL *****\n"; } #### diff -rupN Authen-Radius-0.20_old/Radius.pm Authen-Radius-0.20/Radius.pm --- Authen-Radius-0.20_old/Radius.pm 2012-03-18 20:17:49.313819745 +0100 +++ Authen-Radius-0.20/Radius.pm 2012-03-18 20:18:20.054569761 +0100 @@ -429,7 +429,7 @@ sub vendorID ($) { } else { # look up vendor by attribute name my $vendor_name = $dict_name{$attr->{'Name'}}{'vendor'}; - my $vendor_id = defined ($dict_vendor_name{$vendor_name}{'id'}) ? + my $vendor_id = (defined $vendor_name && defined ($dict_vendor_name{$vendor_name}{'id'})) ? $dict_vendor_name{$vendor_name}{'id'} : 'not defined'; return $vendor_id; } #### diff -rupN RADIUS-1.0_old/RADIUS/Packet.pm RADIUS-1.0/RADIUS/Packet.pm --- RADIUS-1.0_old/RADIUS/Packet.pm 2012-03-18 19:37:52.083326134 +0100 +++ RADIUS-1.0/RADIUS/Packet.pm 2012-03-18 19:40:54.467770068 +0100 @@ -160,6 +160,12 @@ sub unpack { while (length($attrdat)) { my $length = unpack "x C", $attrdat; my ($type, $value) = unpack "C x a${\($length-2)}", $attrdat; + if(!defined($dict->attr_numtype($type))) { + print STDERR "Unknown type $type! Skipping this one - might have dire consequences)!\n"; + print STDERR " Please add type $type to your dictionary!\n"; + substr($attrdat, 0, $length) = ""; + next; + } my $val = &{$unpacker{$dict->attr_numtype($type)}}($value, $type); $self->set_attr($dict->attr_name($type), $val); substr($attrdat, 0, $length) = ""; #### #!/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 { # So we find the rest of our code unshift @INC, "."; }; use Rader; our $APPNAME = "Simple Radius Server"; my $psname = "simpleradius"; print "Changing application name to '$psname'\n\n"; $0 = $psname; my $server = Rader->new( host => 'localhost', port => 1812, proto => 'udp', ); $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; use OATHusers; 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/Access-reject. 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 client. 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; } } 1; #### package OATHusers; #---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 Authen::OATH; sub new { my $class = shift; my $self = bless {}, $class; my %seeds = ( 'tye' => { key_id => '000001', pin => '2412', seed => uc('aaaaaaaaabbbbbbbbbbbbbbccccccccccddddddd'), }, 'browseruk'=> { key_id => '222222', pin => '4242', seed => uc('0101010101010101010101010101010101010101'), }, 'reaper' => { key_id => '9', pin => 'SIGKILL', seed => uc('1231231231231231231231231231231231231231'), }, ); $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) { # Search +/- 5 minutes 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; } 1;