3 # checkrad.pl See if a user is (still) logged in on a certain port.
5 # This is used by the cistron-radius server to check
6 # if its idea of a user logged in on a certain port/nas
7 # is correct if a double login is detected.
9 # Called as: nas_type nas_ip nas_port login session_id
11 # Returns: 0 = no duplicate, 1 = duplicate, >1 = error.
13 # Version: @(#)checkrad.pl 1.18 25-Jul-1999 miquels@cistron.nl
15 # livingston_snmp 1.1 Author: miquels@cistron.nl
16 # portslave_finger 1.0 Author: miquels@cistron.nl
17 # max40xx_finger 1.0 Author: costa@mdi.ca
18 # computone_finger 1.1 Author: costa@mdi.ca
19 # sub tc_tccheck 1.1 Author: alexisv@compass.com.ph
20 # pathras_telnet 1.1 Author: accdias@sst.com.br
21 # patton_snmp 1.0 Author: accdias@sst.com.br
22 # digitro_rusers 1.0 Author: accdias@sst.com.br
23 # usrhiper_snmp 1.0 Author: igor@ipass.net
24 # multitech_snmp 1.0 Author: ehonzay@willmar.com
25 # netserver_telnet 1.0 Author: mts@interplanet.es
26 # versanet_snmp 1.0 Author: support@versanetcomm.com
28 # Config: $debug is the file you want to put debug messages in
29 # $snmpget is the location of your ``snmpget'' program
30 # $naspass is the location of your NAS admin password file
34 $localstatedir = "@localstatedir@";
36 $sysconfdir = "@sysconfdir@";
37 $raddbdir = "@raddbdir@";
40 #$debug = "$logdir/checkrad.log";
42 $snmpget = "@SNMPGET@";
43 $naspass = "$raddbdir/naspasswd";
47 # PM3: $lv_offs is where the last S port is before one or two
48 # ports are skipped (22 or 29, for US or Europe)
49 # $lv_hole is the size of the hole (1 or 2, for US or Europe).
55 # Try to load Net::Telnet, SNMP_Session etc.
56 # Do not complain if we cannot find it.
57 # Prefer a locally installed copy.
60 unshift @INC, "/usr/local/lib/site_perl";
62 eval "use Net::Telnet 3.00;";
63 $::HAVE_NET_TELNET = ($@ eq "");
65 eval "use SNMP_Session;";
68 $::HAVE_SNMP_SESSION = ($@ eq "");
74 # Get password from /etc/raddb/naspasswd file.
75 # Returns (login, password).
78 my ($terminalserver, $emptyok) = @_;
79 my ($login, $password);
80 my ($ts, $log, $pass);
82 unless (open(NFD, $naspass)) {
84 print LOG "checkrad: naspasswd file not found; " .
85 "possible match for $ARGV[3]\n" if ($debug);
86 print STDERR "checkrad: naspassswd file not found; " .
87 "possible match for $ARGV[3]\n";
93 next if (m/^(#|$|[\t ]+$)/);
94 ($ts, $log, $pass) = split(/\s+/, $_, 3);
95 if ($ts eq $terminalserver) {
102 if ($password eq "" && !$emptyok) {
103 print LOG "checkrad: password for $ARGV[1] is null; " .
104 "possible match for $ARGV[3] on " .
105 "port $ARGV[2]\n" if ($debug);
106 print STDERR "checkrad: password for $ARGV[1] is null; " .
107 "possible match for $ARGV[3] on port $ARGV[2]\n";
113 # See if Net::Telnet is there.
115 sub check_net_telnet {
116 if (!$::HAVE_NET_TELNET) {
118 " checkrad: Net::Telnet 3.00+ CPAN module not installed\n"
121 "checkrad: Net::Telnet 3.00+ CPAN module not installed\n";
128 # Do snmpget by calling snmpget.
131 my ($host, $community, $oid) = @_;
135 $_ = `$snmpget $host $community $oid`;
136 if (/^.*(\s|\")([0-9A-Za-z]{8})(\s|\"|$).*$/) {
139 } elsif (/^.*=.*"(.*)"/) {
140 # oid = "...." junk format.
142 } elsif (/^.*=\s*(\S+)/) {
143 # oid = string format
147 # Strip trailing junk if any.
148 $ret =~ s/\s*Hex:.*$//;
153 # Do snmpget by using SNMP_Session.
154 # Coded by Jerry Workman <jerry@newwave.net>
156 sub snmpget_session {
157 my ($host, $community, $OID) = @_;
160 my (@enoid, $var,$response, $bindings, $binding, $value);
161 my ($inoid, $outoid, $upoid, $oid, @retvals);
163 $OID =~ s/^.iso.org.dod.internet.private.enterprises/.1.3.6.1.4.1/;
165 push @enoid, encode_oid((split /\./, $OID));
168 my $session = SNMP_Session->open($host, $community, 161);
169 if (!$session->get_request_response(@enoid)) {
170 $e = "No SNMP answer from $ARGV[0].";
171 print LOG "$e\n" if ($debug);
172 print STDERR "checkrad: $e\n";
175 $response = $session->pdu_buffer;
176 ($bindings) = $session->decode_get_response ($response);
179 ($binding,$bindings) = decode_sequence ($bindings);
180 ($oid,$value) = decode_by_template ($binding, "%O%@");
181 my $tempo = pretty_print($value);
187 push @retvals, $tempo;
198 if ($::HAVE_SNMP_SESSION) {
199 $ret = snmpget_session(@_);
200 } elsif (-x $snmpget) {
201 $ret = snmpget_prog(@_);
203 $e = "Neither SNMP_Session module or $snmpget found!";
204 print LOG "$e\n" if ($debug);
205 print STDERR "checkrad: $e\n";
212 # Strip domains, prefixes and suffixes from username
214 # Known prefixes: (P)PP, (S)LIP e (C)SLIP
215 # Known suffixes: .ppp, .slip e .cslip
217 # Author: Antonio Dias of SST Internet <accdias@sst.com.br>
224 $user =~ s/^\s*(.*?)\s*$/$1/;
226 # Strip out domains, prefix and suffixes
228 $user =~ s/\@(.)*$//;
230 $user =~ s/\.(ppp|slip|cslip)$//;
235 # See if the user is logged in using the Livingston MIB.
236 # We don't check the username but the session ID.
238 $lvm = '.iso.org.dod.internet.private.enterprises.307';
239 sub livingston_snmp {
242 # First find out the offset (ugly!!). Also, if the portno
243 # is greater than 29, substract 2 (S30 and S31 don't exist).
244 # You might need to change this to 23 and 1 for the USA.
246 $_ = snmpget($ARGV[1], "public", "$lvm.3.2.1.1.1.2.5");
249 $portidx = $ARGV[2] + (5 - $xport);
250 $portidx -= $lv_hole if ($ARGV[2] > $lv_offs);
252 print LOG " using $xport offset for port / SNMPno translation\n"
256 # Now get the session id from the terminal server.
258 $sessid = snmpget($ARGV[1], "public", "$lvm.3.2.1.1.1.5.$portidx");
260 print LOG " session id at port S$ARGV[2]: $sessid\n" if ($debug);
262 ($sessid eq $ARGV[4]) ? 1 : 0;
266 # See if the user is logged in using the Cisco MIB
268 $csm = '.iso.org.dod.internet.private.enterprises.9';
270 $login = snmpget($ARGV[1], "public", "$csm.2.9.2.1.18.$ARGV[2]");
272 print LOG " user at port S$ARGV[2]: $login\n" if ($debug);
274 ($login eq $ARGV[3]) ? 1 : 0;
278 # Check a MultiTech CommPlete Server ( CC9600 & CC2400 )
280 # Author: Eric Honzay of Bennett Office Products <ehonzay@willmar.com>
282 $msm = '.iso.org.dod.internet.private.enterprises.995';
284 my $temp = $ARGV[2] + 1;
286 $login = snmpget($ARGV[1], "public", "$msm.2.31.1.1.1.$temp");
287 print LOG " user at port S$ARGV[2]: $login\n" if ($debug);
289 ($login eq $ARGV[3]) ? 1 : 0;
293 # Check a Computone Powerrack via finger
295 # Author: Shiloh Costa of MDI Internet Inc. <costa@mdi.ca>
297 sub computone_finger {
299 open(FD, "finger \@$ARGV[1]|");
304 for ($num = 0; $num < $numline; $num++) {
305 if( $line[$num] =~ / $ARGV[3] / ){
306 #print "$ARGV[3] is online\n";
307 $online = 1; # user is online
315 # Check an Ascend Max4000 or similar model via finger
317 # Note: Not all software revisions support finger
318 # You may also need to enable the finger option.
320 # Author: Shiloh Costa of MDI Internet Inc. <costa@mdi.ca>
323 open(FD, "finger $ARGV[3]\@$ARGV[1]|");
326 if( $line =~ /Session/ ){
327 return 1; # user is online
329 return 0; # user is offline
336 # See if the user is logged in using the portslave finger.
338 sub portslave_finger {
343 open(FD, "finger \@$ARGV[1]|");
346 # Check for ^Port. If we don't see it we
347 # wont get confused by non-portslave-finger
354 next if (!$Port_seen);
357 ($port, $user) = /^.(...) (...............)/;
361 $ulen = length($user);
363 # HACK: strip [PSC] from the front of the username,
364 # and things like .ppp from the end.
367 $user =~ s/\.(ppp|slip|cslip)$//;
370 # HACK: because ut_user usually has max. 8 characters
371 # we only compare up the the length of $user if the
372 # unstripped name had 8 chars.
374 $argv_user = $ARGV[3];
376 $ulen = length($user);
377 $argv_user = substr($ARGV[3], 0, $ulen);
380 if ($port == $ARGV[2]) {
381 if ($user eq $argv_user) {
382 print LOG " $user matches $argv_user " .
383 "on port $port" if ($debug);
387 print LOG " $user doesn't match $argv_user " .
388 "on port $port" if ($debug);
399 # See if the user is already logged-in at the 3Com/USR Total Control.
400 # (this routine by Alexis C. Villalon <alexisv@compass.com.ph>).
401 # You must have the Net::Telnet module from CPAN for this to work.
402 # You must also have your /etc/raddb/naspasswd made up.
406 # Localize all variables first.
408 my ($Port_seen, $ts, $terminalserver, $log, $login, $pass, $password);
409 my ($telnet, $curprompt, $curline, $ok, $totlines, $ccntr);
410 my (@curlines, @cltok, $user, $port, $ulen);
412 return 2 unless (check_net_telnet());
414 $terminalserver = $ARGV[1];
417 # Get login name and password for a certain NAS from $naspass.
419 ($login, $password) = naspasswd($terminalserver, 1);
420 return 2 if ($password eq "");
423 # Communicate with NAS using Net::Telnet, then issue
424 # the command "show sessions" to see who are logged in.
425 # Thanks to Chris Jackson <chrisj@tidewater.net> for the
426 # for the "-- Press Return for More --" workaround.
428 $telnet = new Net::Telnet (Timeout => 10,
430 $telnet->open($terminalserver);
431 $telnet->login($login, $password);
432 $telnet->print("show sessions");
433 while ($curprompt ne "\>") {
434 ($curline, $curprompt) = $telnet->waitfor
435 (String => "-- Press Return for More --",
438 $ok = $telnet->print("");
439 push @curlines, split(/^/m, $curline);
443 # Telnet closed. We got the info. Let's examine it.
445 $totlines = @curlines;
447 while($ccntr < $totlines) {
451 if ($curlines[$ccntr] =~ /^Port/) {
457 # Ignore all unnecessary lines.
459 if (!$Port_seen || $curlines[$ccntr] =~ /^---/ ||
460 $curlines[$ccntr] =~ /^ .*$/) {
465 # Parse the current line for the port# and username.
467 @cltok = split(/\s+/, $curlines[$ccntr]);
471 $ulen = length($user);
473 # HACK: strip [PSC] from the front of the username,
474 # and things like .ppp from the end. Strip S from
475 # the front of the port number.
478 $user =~ s/\.(ppp|slip|cslip)$//;
481 # HACK: because "show sessions" shows max. 15 characters
482 # we only compare up to the length of $user if the
483 # unstripped name had 15 chars.
485 $argv_user = $ARGV[3];
487 $ulen = length($user);
488 $argv_user = substr($ARGV[3], 0, $ulen);
490 if ($port == $ARGV[2]) {
491 if ($user eq $argv_user) {
492 print LOG " $user matches $argv_user " .
493 "on port $port" if ($debug);
496 print LOG " $user doesn't match $argv_user " .
497 "on port $port" if ($debug);
506 # Check a Cyclades PathRAS via telnet
510 # Author: Antonio Dias of SST Internet <accdias@sst.com.br>
514 # Localize all variables first.
516 my (@users, $pathras, $port, $errmsg, $user, $login, $password);
518 # Do we have Net::Telnet installed?
520 return 2 unless (check_net_telnet());
522 # Get login name and password for NAS from $naspass file.
524 ($login, $password) = naspasswd($ARGV[1], 1);
526 # NAS doesn't have a password defined
528 return 2 if ($password eq "");
530 # Communicate with PathRAS using Net::Telnet, then access
531 # menu option 6.8 to see who are logged in. This assumes
532 # you are using the default Prompt String (menu option 6.2)
533 # and is based on PathRAS firmware version 1.1.9g
536 $pathras = new Net::Telnet (
538 Prompt => '/(.*)\s:*$/',
544 if (!$pathras->open($ARGV[1])) {;
545 print LOG " Error: " . $pathras->errmsg . "\n" if ($debug);
550 # Dunno why but PathRAS only issue its banner and
551 # the login prompt after receive a "CR"
553 if (!$pathras->print("")) {
554 print LOG " Error: " . $pathras->errmsg . "\n" if ($debug);
562 if (!($pathras->login($login, $password))) {
563 print LOG " Error: " . $pathras->errmsg . "\n" if ($debug);
568 # Access menu option 6 "PathRAS Management"
570 if (!($pathras->cmd(String => '6',
571 Prompt => '/ ==\> $/',
573 print LOG " Error: " . $pathras->errmsg . "\n" if ($debug);
578 # Access menu option 8 "Show Active Ports"
580 if (!(@users = $pathras->cmd(String => '8',
581 Prompt => '/Type \<enter\>/',
583 print LOG " Error: " . $pathras->errmsg . "\n" if ($debug);
588 # Since we got the info we want, let's close the session
592 # Telnet closed. We got the info so let's examine it.
595 if (m/PPP Active|SLIP Active/) {
596 ($port, $user) = split;
598 # Strip out any prefixes and suffixes
601 $user = strip_username($user);
603 # Check if $user is already connected
605 if ($user eq $ARGV[3]) {
606 print LOG " User $ARGV[3] found on " .
607 "NAS $ARGV[1] port $port.\n" if ($debug);
612 print LOG " User $ARGV[3] not found on " .
613 "NAS $ARGV[1].\n" if ($debug);
618 # Check a Patton 2800 via snmp
622 # Author: Antonio Dias of SST Internet <accdias@sst.com.br>
627 $oid = '.1.3.6.1.4.1.1768.5.100.1.40.' . hex $ARGV[4];
629 # Check if the session still active
631 if (snmpget($ARGV[1], "monitor", "$oid") == 0) {
632 print LOG " Session $ARGV[4] still active on NAS " .
633 "$ARGV[1], port $ARGV[2], for user $ARGV[3].\n" if ($debug);
640 # Check a Digitro BXS via rusers
644 # Author: Antonio Dias of SST Internet <accdias@sst.com.br>
647 if (-e $rusers && -x $rusers) {
648 open(FD, "$rusers $ARGV[1]|");
650 print LOG " Error: can't execute $rusers\n" if $debug;
665 # 3Com/USR HiPer Arc Total Control.
666 # This works with HiPer Arc 4.0.30
667 # (this routine by Igor Brezac <igor@ipass.net>)
669 $usrm = '.iso.org.dod.internet.private.enterprises.429';
672 # Somebody please verify????
673 my($oidext) = $ARGV[2] + 1256;
676 $login = snmpget($ARGV[1], "public", "$usrm.4.10.1.1.18.$oidext");
678 print LOG " user at port S$ARGV[2]: $login\n" if ($debug);
680 ($login eq $ARGV[3]) ? 1 : 0;
685 # Check USR Netserver with Telnet - based on tc_tccheck.
686 # By "Marti" <mts@interplanet.es>
690 # Localize all variables first.
692 my ($ts, $terminalserver, $login, $password);
693 my ($telnet, $curprompt, $curline, $ok);
694 my (@curlines, $user, $port);
696 return 2 unless (check_net_telnet());
698 $terminalserver = $ARGV[1];
701 # Get login name and password for a certain NAS from $naspass.
703 ($login, $password) = naspasswd($terminalserver, 1);
704 return 2 if ($password eq "");
707 # Communicate with Netserver using Net::Telnet, then access
708 # list connectionsto see who are logged in.
710 $telnet = new Net::Telnet (Timeout => 10,
712 $telnet->open($terminalserver);
717 $telnet->login($login, $password);
720 # Launch list connections command
722 $telnet->print("list connections");
724 while ($curprompt ne "\>") {
725 ($curline, $curprompt) = $telnet->waitfor
728 $ok = $telnet->print("");
729 push @curlines, split(/^/m, $curline);
734 # Telnet closed. We got the info. Let's examine it.
738 ($port, $user, $dummy) = split;
740 # Strip out any prefixes and suffixes
743 # uncomment this if you use the standard
745 #$user =~ s/^[PSC]//;
746 #$user =~ s/\.(ppp|slip|cslip)$//;
748 # Check to see if $user is already connected
750 if ($user eq $ARGV[3]) {
751 print LOG " $user matches $ARGV[3] " .
752 "on port $port" if ($debug);
758 " $ARGV[3] not found on Netserver logged users list " if ($debug);
763 # Versanet's Perl Script Support:
765 # ___ versanet_snmp 1.0 by support@versanetcomm.com ___ July 1999
766 # Versanet Enterprise MIB Base: 1.3.6.1.4.1.2180
768 # VN2001/2002 use slot/port number to locate modems. To use snmp get we
769 # have to translate the original port number into a slot/port pair.
771 $vsm = '.iso.org.dod.internet.private.enterprises.2180';
774 print LOG "argv[2] = $ARGV[2] " if ($debug);
776 $port = 8 if ($port eq 0);
777 print LOG "port = $port " if ($debug);
778 $slot = (($ARGV[2]-$port)/8)+1;
779 print LOG "slot = $slot" if ($debug);
780 $loginname = snmpget($ARGV[1], "public", "$vsm.27.1.1.3.$slot.$port");
782 # Note: the "public" string above could be replaced by the public
783 # community string defined in Versanet VN2001/VN2002.
785 print LOG " user at slot $slot port $port: $loginname\n" if ($debug); ($loginname eq $ARGV[3]) ? 1 : 0;
790 open(LOG, ">>$debug");
792 print LOG "$now checkrad @ARGV\n";
796 print LOG "Usage: checkrad nas_type nas_ip " .
797 "nas_port login session_id\n" if ($debug);
798 print STDERR "Usage: checkrad nas_type nas_ip " .
799 "nas_port login session_id\n";
800 close LOG if ($debug);
804 if ($ARGV[0] eq 'livingston') {
805 $ret = &livingston_snmp;
806 } elsif ($ARGV[0] eq 'cisco') {
808 } elsif ($ARGV[0] eq 'multitech') {
809 $ret = &multitech_snmp;
810 } elsif ($ARGV[0] eq 'computone') {
811 $ret = &computone_finger;
812 } elsif ($ARGV[0] eq 'max40xx') {
813 $ret = &max40xx_finger;
814 } elsif ($ARGV[0] eq 'portslave') {
815 $ret = &portslave_finger;
816 } elsif ($ARGV[0] eq 'tc') {
818 } elsif ($ARGV[0] eq 'pathras') {
819 $ret = &pathras_telnet;
820 } elsif ($ARGV[0] eq 'patton') {
822 } elsif ($ARGV[0] eq 'digitro') {
823 $ret = &digitro_rusers;
824 } elsif ($ARGV[0] eq 'usrhiper') {
825 $ret = &usrhiper_snmp;
826 } elsif ($ARGV[0] eq 'netserver') {
827 $ret = &usrnet_telnet;
828 } elsif ($ARGV[0] eq 'versanet'){
829 $ret = &versanet_snmp;
830 } elsif ($ARGV[0] eq 'other') {
833 print LOG " checkrad: unknown NAS type $ARGV[0]\n" if ($debug);
834 print STDERR "checkrad: unknown NAS type $ARGV[0]\n";
840 $mn = "double detected" if ($ret == 1);
841 $mn = "error detected" if ($ret == 2);
842 print LOG " Returning $ret ($mn)\n";