Get rid of references to my old address
[freeradius.git] / src / main / checkrad.pl.in
1 #! @PERL@
2 #
3 # checkrad.pl   See if a user is (still) logged in on a certain port.
4 #
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.
8 #
9 # Called as:    nas_type nas_ip nas_port login session_id
10 #
11 # Returns:      0 = no duplicate, 1 = duplicate, >1 = error.
12 #
13 # Version:      $Id$
14 #
15 #               livingston_snmp  1.1    Author: miquels@cistron.nl
16 #               cvx_snmp         1.0    Author: miquels@cistron.nl
17 #               portslave_finger 1.0    Author: miquels@cistron.nl
18 #               max40xx_finger   1.0    Author: costa@mdi.ca
19 #               ascend_snmp      1.1    Author: blaz@amis.net
20 #               computone_finger 1.2    Author: pacman@world.std.com
21 #               sub tc_tccheck   1.1    Author: alexisv@compass.com.ph
22 #               cyclades_telnet  1.2    Author: accdias@sst.com.br
23 #               patton_snmp      1.0    Author: accdias@sst.com.br
24 #               digitro_rusers   1.1    Author: accdias@sst.com.br
25 #               cyclades_snmp    1.0    Author: accdias@sst.com.br
26 #               usrhiper_snmp    1.0    Author: igor@ipass.net
27 #               multitech_snmp   1.0    Author: ehonzay@willmar.com
28 #               netserver_telnet 1.0    Author: mts@interplanet.es
29 #               versanet_snmp    1.0    Author: support@versanetcomm.com
30 #               bay_finger       1.0    Author: chris@shenton.org
31 #
32 #       Config: $debug is the file you want to put debug messages in
33 #               $snmpget is the location of your ``snmpget'' program
34 #               $snmpwalk is the location of your ``snmpwalk'' program
35 #               $rusers is the location of your ``rusers'' program
36 #               $naspass is the location of your NAS admin password file
37 #
38
39 $prefix         = "@prefix@";
40 $localstatedir  = "@localstatedir@";
41 $logdir         = "@logdir@";
42 $sysconfdir     = "@sysconfdir@";
43 $raddbdir       = "@raddbdir@";
44
45 $debug          = "";
46 #$debug         = "$logdir/checkrad.log";
47
48 $snmpget        = "@SNMPGET@";
49 $snmpwalk       = "@SNMPWALK@";
50 $rusers         = "@RUSERS@";
51 $naspass        = "$raddbdir/naspasswd";
52
53 #
54 #       PM3:    $lv_offs is where the last S port is before one or two
55 #               ports are skipped (22 or 29, for US or Europe)
56 #               $lv_hole is the size of the hole (1 or 2, for US or Europe).
57 #
58 $lv_offs = 29;
59 $lv_hole = 2;
60
61 #
62 #       USR-Hiper: $hiper_density is the reported port density (default 256
63 #       but 24 makes more sense)
64 #
65 $hiper_density = 256;
66
67 #
68 #       Try to load Net::Telnet, SNMP_Session etc.
69 #       Do not complain if we cannot find it.
70 #       Prefer a locally installed copy.
71 #
72 BEGIN {
73         unshift @INC, "/usr/local/lib/site_perl";
74
75         eval "use Net::Telnet 3.00;";
76         $::HAVE_NET_TELNET = ($@ eq "");
77
78         eval "use SNMP_Session;";
79         if ($@ eq "") {
80                 eval "use BER;";
81                 $::HAVE_SNMP_SESSION = ($@ eq "");
82                 eval "use Socket;";
83         }
84 };
85
86 #
87 #       Get password from /etc/raddb/naspasswd file.
88 #       Returns (login, password).
89 #
90 sub naspasswd {
91         my ($terminalserver, $emptyok) = @_;
92         my ($login, $password);
93         my ($ts, $log, $pass);
94
95         unless (open(NFD, $naspass)) {
96                 if (!$emptyok) {
97                         print LOG "checkrad: naspasswd file not found; " .
98                         "possible match for $ARGV[3]\n" if ($debug);
99                         print STDERR "checkrad: naspasswd file not found; " .
100                         "possible match for $ARGV[3]\n";
101                 }
102                 return ();
103         }
104         while (<NFD>) {
105                 chop;
106                 next if (m/^(#|$|[\t ]+$)/);
107                 ($ts, $log, $pass) = split(/\s+/, $_, 3);
108                 if ($ts eq $terminalserver) {
109                         $login = $log;
110                         $password = $pass;
111                         last;
112                 }
113         }
114         close NFD;
115         if ($password eq "" && !$emptyok) {
116                 print LOG "checkrad: password for $ARGV[1] is null; " .
117                         "possible match for $ARGV[3] on " . 
118                         "port $ARGV[2]\n" if ($debug);
119                 print STDERR "checkrad: password for $ARGV[1] is null; " .
120                         "possible match for $ARGV[3] on port $ARGV[2]\n"; 
121         }
122         ($login, $password);
123 }
124
125 #
126 #       See if Net::Telnet is there.
127 #
128 sub check_net_telnet {
129         if (!$::HAVE_NET_TELNET) {
130                 print LOG
131                 "  checkrad: Net::Telnet 3.00+ CPAN module not installed\n"
132                 if ($debug);
133                 print STDERR
134                 "checkrad: Net::Telnet 3.00+ CPAN module not installed\n";
135                 return 0;
136         }
137         1;
138 }
139
140 #
141 #       Do snmpget by calling snmpget.
142 #
143 sub snmpget_prog {
144         my ($host, $community, $oid) = @_;
145         my ($ret);
146         local $_;
147
148         $_ = `$snmpget $host $community $oid`;
149         if (/^.*(\s|\")([0-9A-Za-z]{8})(\s|\"|$).*$/) {
150                 # Session ID format.
151                 $ret = $2;
152         } elsif (/^.*=.*"(.*)"/) {
153                 # oid = "...." junk format.
154                 $ret = $1;
155         } elsif (/^.*=\s*(\S+)/) {
156                 # oid = string format
157                 $ret = $1;
158         }
159
160         # Strip trailing junk if any.
161         $ret =~ s/\s*Hex:.*$//;
162         $ret;
163 }
164
165 #
166 #       Do snmpget by using SNMP_Session.
167 #       Coded by Jerry Workman <jerry@newwave.net>
168 #
169 sub snmpget_session {
170         my ($host, $community, $OID) = @_;
171         my ($ret);
172         local $_;
173         my (@enoid, $var,$response, $bindings, $binding, $value);
174         my ($inoid, $outoid, $upoid, $oid, @retvals);
175
176         $OID =~ s/^.iso.org.dod.internet.private.enterprises/.1.3.6.1.4.1/;
177
178         push @enoid,  encode_oid((split /\./, $OID));
179         srand();
180
181         my $session = SNMP_Session->open($host, $community, 161);
182         if (!$session->get_request_response(@enoid)) {
183                 $e = "No SNMP answer from $ARGV[0].";
184                 print LOG "$e\n" if ($debug);
185                 print STDERR "checkrad: $e\n";
186                 return "";
187         }
188         $response = $session->pdu_buffer;
189         ($bindings) = $session->decode_get_response ($response);
190         $session->close ();
191         while ($bindings) {
192                 ($binding,$bindings) = decode_sequence ($bindings);
193                 ($oid,$value) = decode_by_template ($binding, "%O%@");
194                 my $tempo = pretty_print($value);
195                 $tempo=~s/\t/ /g;
196                 $tempo=~s/\n/ /g;
197                 $tempo=~s/^\s+//;
198                 $tempo=~s/\s+$//;
199
200                 push @retvals, $tempo;
201         }
202         $retvals[0];
203 }
204
205 #
206 #       Do snmpget
207 #
208 sub snmpget {
209         my $ret;
210
211         if ($::HAVE_SNMP_SESSION) {
212                 $ret = snmpget_session(@_);
213         } elsif (-x $snmpget) {
214                 $ret = snmpget_prog(@_);
215         } else {
216                 $e = "Neither SNMP_Session module or $snmpget found!";
217                 print LOG "$e\n" if ($debug);
218                 print STDERR "checkrad: $e\n";
219                 $ret = "";
220         }
221         $ret;
222 }
223
224 #
225 #       Strip domains, prefixes and suffixes from username
226 #       
227 #       Known prefixes: (P)PP, (S)LIP e (C)SLIP
228 #       Known suffixes: .ppp, .slip e .cslip
229 #
230 #       Author: Antonio Dias of SST Internet <accdias@sst.com.br>
231 #
232 sub strip_username {
233         my ($user) = @_;
234         #
235         #       Trim white spaces.
236         #
237         $user =~ s/^\s*(.*?)\s*$/$1/;
238         #
239         #       Strip out domains, prefix and suffixes
240         #
241         $user =~ s/\@(.)*$//;
242         $user =~ s/^[PSC]//;
243         $user =~ s/\.(ppp|slip|cslip)$//;
244         $user;
245 }
246
247 #
248 #       See if the user is logged in using the Livingston MIB.
249 #       We don't check the username but the session ID.
250 #
251 $lvm     = '.iso.org.dod.internet.private.enterprises.307';
252 sub livingston_snmp {
253
254         #
255         #       First find out the offset (ugly!!). Also, if the portno
256         #       is greater than 29, substract 2 (S30 and S31 don't exist).
257         #       You might need to change this to 23 and 1 for the USA.
258         #
259         $_ = snmpget($ARGV[1], "public", "$lvm.3.2.1.1.1.2.5");
260         /S([0-9]+)/;
261         $xport = $1 + 0;
262         $portidx = $ARGV[2] + (5 - $xport);
263         $portidx -= $lv_hole if ($ARGV[2] > $lv_offs);
264         chop;
265         print LOG "  using $xport offset for port / SNMPno translation\n"
266                 if ($debug);
267
268         #
269         #       Now get the session id from the terminal server.
270         #
271         $sessid = snmpget($ARGV[1], "public", "$lvm.3.2.1.1.1.5.$portidx");
272
273         print LOG "  session id at port S$ARGV[2]: $sessid\n" if ($debug);
274
275         ($sessid eq $ARGV[4]) ? 1 : 0;
276 }
277
278 #
279 #       See if the user is logged in using the Aptis MIB.
280 #       We don't check the username but the session ID.
281 #
282 # sessionStatusActiveName
283 $apm1    = '.iso.org.dod.internet.private.enterprises.2637.2.2.102.1.12';
284 # sessionStatusActiveStopTime
285 $apm2    = '.iso.org.dod.internet.private.enterprises.2637.2.2.102.1.20';
286 sub cvx_snmp {
287
288         # Remove unique identifier, then take remainder of the
289         # session-id as a hex number, convert that to decimal.
290         my $sessid = $ARGV[4];
291         $sessid =~ s/^.*://;
292         $sessid =~ s/^0*//;
293         $sessid = "0" if ($sessid eq '');
294
295         #
296         #       Now get the login from the terminal server.
297         #       Blech - the SNMP table is called 'sessionStatusActiveTable,
298         #       but it sometimes lists inactive sessions too.
299         #       However an active session doesn't have a Stop time,
300         #       so we can differentiate that way.
301         #
302         my $login = snmpget($ARGV[1], "public", "$apm1." . hex($sessid));
303         my $stopt = snmpget($ARGV[1], "public", "$apm2." . hex($sessid));
304         $login = "--" if ($stopt > 0);
305
306         print LOG "  login with session-id $ARGV[4]: $login\n" if ($debug);
307
308         (strip_username($login) eq strip_username($ARGV[3])) ? 1 : 0;
309 }
310
311 #
312 #       See if the user is logged in using the Cisco MIB
313 #
314 $csm     = '.iso.org.dod.internet.private.enterprises.9';
315 sub cisco_snmp {
316
317         # Look up community string in naspasswd file.
318         my ($login, $password) = naspasswd($ARGV[1], 1);
319         if ($login && $login ne 'SNMP') {
320                 if ($debug) {
321                         print LOG
322                         "   Error: Need SNMP community string for $ARGV[1]\n";
323                 }
324                 return 2;
325         } else {
326                 $password = "public";
327         }
328
329         $login = snmpget($ARGV[1], $password, "$csm.2.9.2.1.18.$ARGV[2]");
330
331         print LOG "  user at port S$ARGV[2]: $login\n" if ($debug);
332
333         ($login eq $ARGV[3]) ? 1 : 0;
334 }
335
336 #
337 #       Check a MultiTech CommPlete Server ( CC9600 & CC2400 )
338 #
339 #       Author: Eric Honzay of Bennett Office Products <ehonzay@willmar.com>
340 #
341 $msm    = '.iso.org.dod.internet.private.enterprises.995';
342 sub multitech_snmp {
343         my $temp = $ARGV[2] + 1;
344
345         $login = snmpget($ARGV[1], "public", "$msm.2.31.1.1.1.$temp");
346         print LOG " user at port S$ARGV[2]: $login\n" if ($debug);
347
348         ($login eq $ARGV[3]) ? 1 : 0;
349 }
350
351 #
352 #       Check a Computone Powerrack via finger
353 #
354 #       Old Author: Shiloh Costa of MDI Internet Inc. <costa@mdi.ca>
355 #       New Author: Alan Curry <pacman@world.std.com>
356 #
357 # The finger response format is version-dependent. To do this *right*, you
358 # need to know exactly where the port number and username are. I know that
359 # for 1.7.2, and 3.0.4 but for others I just guess.
360 # Oh yeah and on top of it all, the thing truncates usernames. --Pac.
361 #
362 # 1.7.2 and 3.0.4 both look like this:
363 #
364 # 0    0 000 00:56 luser         pppfsm  Incoming PPP, ppp00, 10.0.0.1
365 #
366 # and the truncated ones look like this:
367 #
368 # 25   0 000 00:15 longnameluse..pppfsm  Incoming PPP, ppp25, 10.0.0.26
369 #
370 # Yes, the fields run together. Long Usernames Considered Harmful.
371 #
372 sub computone_finger {
373         my $trunc, $ver;
374
375         open(FD, "finger \@$ARGV[1]|") or return 2;
376         <FD>; # the [hostname] line is definitely uninteresting
377         $trunc = substr($ARGV[3], 0, 12);
378         $ver = "";
379         while(<FD>) {
380                 if(/cnx kernel release ([^ ,]+)[, ]/) {
381                         $ver = $1;
382                         next;
383                 }
384                 # Check for known versions
385                 if ($ver eq '1.7.2' || $ver eq '3.0.4') {
386                         if (/^\Q$ARGV[2]\E\s+\S+\s+\S+\s+\S+\s+\Q$trunc\E(\s+|\.\.)/) {
387                                 close FD;
388                                 return 1;
389                         }
390                         next;
391                 }
392                 # All others.
393                 if (/^\s*\Q$ARGV[2]\E\s+.*\s+\Q$trunc\E\s+/) {
394                         close FD;
395                         return 1;
396                 }
397         }
398
399         close FD;
400         return 0;
401 }
402
403 #
404 #       Check an Ascend Max4000 or similar model via finger
405 #
406 #       Note: Not all software revisions support finger
407 #             You may also need to enable the finger option.
408 #
409 #       Author: Shiloh Costa of MDI Internet Inc. <costa@mdi.ca>
410 #
411 sub max40xx_finger {
412         open(FD, "finger $ARGV[3]\@$ARGV[1]|");
413         while(<FD>) {
414            $line = <FD>;
415            if( $line =~ /Session/ ){
416               return 1; # user is online
417            }else{
418               return 0; # user is offline
419            }
420         }
421         close FD;
422 }
423
424
425 #
426 #       Check an Ascend Max4000 or similar model via SNMP
427 #
428 #       Author: Blaz Zupan of Medinet <blaz@amis.net>
429 #
430 $asm   = '.iso.org.dod.internet.private.enterprises.529';
431 sub ascend_snmp {
432         my $sess_id;
433         my $l1, $l2;
434
435         $l1 = '';
436         $l2 = '';
437
438         #
439         #       If it looks like hex, only try it as hex,
440         #       otherwise try it as both decimal and hex.
441         #
442         $sess_id = $ARGV[4];
443         if ($sess_id !~ /^0/ && $sess_id !~ /[a-f]/i) {
444                 $l1 = snmpget($ARGV[1], "public", "$asm.12.3.1.4.$sess_id");
445         }
446         $sess_id = hex $ARGV[4];
447         $l2 = snmpget($ARGV[1], "public", "$asm.12.3.1.4.$sess_id");
448
449         print LOG "  user at port S$ARGV[2]: $l1 (dec)\n" if ($debug && $l1);
450         print LOG "  user at port S$ARGV[2]: $l2 (hex)\n" if ($debug && $l2);
451
452         (($l1 && $l1 eq $ARGV[3]) || ($l2 && $l2 eq $ARGV[3])) ? 1 : 0;
453 }
454
455
456 #
457 #       See if the user is logged in using the portslave finger.
458 #
459 sub portslave_finger {
460         my ($Port_seen);
461
462         $Port_seen = 0;
463
464         open(FD, "finger \@$ARGV[1]|");
465         while(<FD>) {
466                 #
467                 #       Check for ^Port. If we don't see it we
468                 #       wont get confused by non-portslave-finger
469                 #       output too.
470                 #
471                 if (/^Port/) {
472                         $Port_seen++;
473                         next;
474                 }
475                 next if (!$Port_seen);
476                 next if (/^---/);
477
478                 ($port, $user) = /^.(...) (...............)/;
479
480                 $port =~ s/ .*//;
481                 $user =~ s/ .*//;
482                 $ulen = length($user);
483                 #
484                 #       HACK: strip [PSC] from the front of the username,
485                 #       and things like .ppp from the end.
486                 #
487                 $user =~ s/^[PSC]//;
488                 $user =~ s/\.(ppp|slip|cslip)$//;
489
490                 #
491                 #       HACK: because ut_user usually has max. 8 characters
492                 #       we only compare up the the length of $user if the
493                 #       unstripped name had 8 chars.
494                 #
495                 $argv_user = $ARGV[3];
496                 if ($ulen == 8) {
497                         $ulen = length($user);
498                         $argv_user = substr($ARGV[3], 0, $ulen);
499                 }
500
501                 if ($port == $ARGV[2]) {
502                         if ($user eq $argv_user) {
503                                 print LOG "  $user matches $argv_user " .
504                                         "on port $port" if ($debug);
505                                 close FD;
506                                 return 1;
507                         } else {
508                                 print LOG "  $user doesn't match $argv_user " .
509                                         "on port $port" if ($debug);
510                                 close FD;
511                                 return 0;
512                         }
513                 }
514         }
515         close FD;
516         0;
517 }
518
519 #
520 #       See if the user is already logged-in at the 3Com/USR Total Control.
521 #       (this routine by Alexis C. Villalon <alexisv@compass.com.ph>).
522 #       You must have the Net::Telnet module from CPAN for this to work.
523 #       You must also have your /etc/raddb/naspasswd made up.
524
525 sub tc_tccheck {
526         #
527         #       Localize all variables first.
528         #
529         my ($Port_seen, $ts, $terminalserver, $log, $login, $pass, $password);
530         my ($telnet, $curprompt, $curline, $ok, $totlines, $ccntr);
531         my (@curlines, @cltok, $user, $port, $ulen);
532
533         return 2 unless (check_net_telnet());
534
535         $terminalserver = $ARGV[1];
536         $Port_seen = 0;
537         #
538         #       Get login name and password for a certain NAS from $naspass.
539         #
540         ($login, $password) = naspasswd($terminalserver, 1);
541         return 2 if ($password eq "");
542
543         #
544         #       Communicate with NAS using Net::Telnet, then issue
545         #       the command "show sessions" to see who are logged in.
546         #       Thanks to Chris Jackson <chrisj@tidewater.net> for the
547         #       for the "-- Press Return for More --" workaround.
548         #
549         $telnet = new Net::Telnet (Timeout => 10,
550                                    Prompt => '/\>/');
551         $telnet->open($terminalserver);
552         $telnet->login($login, $password);
553         $telnet->print("show sessions");
554         while ($curprompt ne "\>") {
555                 ($curline, $curprompt) = $telnet->waitfor
556                         (String => "-- Press Return for More --",
557                          String => "\>",
558                          Timeout => 10);
559                 $ok = $telnet->print("");
560                 push @curlines, split(/^/m, $curline);
561         }
562         $telnet->close;
563         #
564         #       Telnet closed.  We got the info.  Let's examine it.
565         #
566         $totlines = @curlines;
567         $ccntr = 0;
568         while($ccntr < $totlines) {
569                 #
570                 #       Check for ^Port.
571                 #
572                 if ($curlines[$ccntr] =~ /^Port/) {
573                         $Port_seen++;
574                         $ccntr++;
575                         next;
576                 }
577                 #
578                 #       Ignore all unnecessary lines.
579                 #
580                 if (!$Port_seen || $curlines[$ccntr] =~ /^---/ ||
581                         $curlines[$ccntr] =~ /^ .*$/) {
582                         $ccntr++;
583                         next;
584                 }
585                 #
586                 #       Parse the current line for the port# and username.
587                 #
588                 @cltok = split(/\s+/, $curlines[$ccntr]);
589                 $ccntr++;
590                 $port = $cltok[0];
591                 $user = $cltok[1];
592                 $ulen = length($user);
593                 #
594                 #       HACK: strip [PSC] from the front of the username,
595                 #       and things like .ppp from the end.  Strip S from
596                 #       the front of the port number.
597                 #
598                 $user =~ s/^[PSC]//;
599                 $user =~ s/\.(ppp|slip|cslip)$//;
600                 $port =~ s/^S//;
601                 #               
602                 #       HACK: because "show sessions" shows max. 15 characters
603                 #       we only compare up to the length of $user if the
604                 #       unstripped name had 15 chars.
605                 #
606                 $argv_user = $ARGV[3];
607                 if ($ulen == 15) {
608                         $ulen = length($user);
609                         $argv_user = substr($ARGV[3], 0, $ulen);
610                 }
611                 if ($port == $ARGV[2]) {
612                         if ($user eq $argv_user) {
613                                 print LOG "  $user matches $argv_user " .
614                                         "on port $port" if ($debug);
615                                 return 1;
616                         } else {
617                                 print LOG "  $user doesn't match $argv_user " .
618                                         "on port $port" if ($debug);
619                                 return 0;
620                         }
621                 }
622         }
623         0;
624 }
625
626 #
627 #       Check a Cyclades PathRAS via telnet
628 #
629 #       Version: 1.2
630 #
631 #       Author: Antonio Dias of SST Internet <accdias@sst.com.br>
632 #
633 sub cyclades_telnet {
634         #
635         #       Localize all variables first.
636         #
637         my ($pr, $pr_login, $pr_passwd, $pr_prompt, $endlist, @list, $port, $user);
638         #
639         #       This variable must match PathRAS' command prompt 
640         #       string as entered in menu option 6.2.
641         #       The value below matches the default command prompt.
642         #
643         $pr_prompt = '/Select option ==\>$/i';
644
645         #
646         #       This variable match the end of userslist.
647         #
648         $endlist = '/Type \<enter\>/i';
649
650         #
651         #       Do we have Net::Telnet installed?
652         #
653         return 2 unless (check_net_telnet());
654
655         #
656         #       Get login name and password for NAS 
657         #       from $naspass file.
658         #
659         ($pr_login, $pr_passwd) = naspasswd($ARGV[1], 1);
660
661         #
662         #       Communicate with PathRAS using Net::Telnet, then access
663         #       menu option 6.8 to see who are logged in.
664         #       Based on PathRAS firmware version 1.2.3
665         #
666         $pr = new Net::Telnet (
667                 Timeout         => 10,
668                 Host            => $ARGV[1],
669                 ErrMode         => 'return'
670         ) || return 2;
671
672         #
673         #       Force PathRAS shows its banner.
674         #
675         $pr->break();
676
677         #
678         #       Log on PathRAS
679         #
680         if ($pr->waitfor(Match => '/login : $/i') == 1) {
681                 $pr->print($pr_login);
682         } else { 
683                 print LOG " Error: sending login name to PathRAS\n" if ($debug);
684                 $pr->close;
685                 return 2;       
686         }
687
688         if ($pr->waitfor(Match => '/password : $/i') == 1) {
689                 $pr->print($pr_passwd);
690         } else { 
691                 print LOG " Error: sending password to PathRAS.\n" if ($debug);
692                 $pr->close;
693                 return 2;       
694         }
695
696         $pr->print();
697
698         #
699         #       Access menu option 6 "PathRAS Management"
700         #
701         if ($pr->waitfor(Match => $pr_prompt) == 1) {
702                 $pr->print('6');
703         } else { 
704                 print LOG "  Error: acessing menu option '6'.\n" if ($debug);
705                 $pr->close;
706                 return 2;
707         }
708         #
709         #       Access menu option 8 "Show Active Ports"
710         #
711         if ($pr->waitfor(Match => $pr_prompt) == 1) {
712                 @list = $pr->cmd(String => '8', Prompt => $endlist);
713         } else { 
714                 print LOG "  Error: acessing menu option '8'.\n" if ($debug);
715                 $pr->close;
716                 return 2;
717         }
718         #
719         #       Since we got the info we want, let's close 
720         #       the telnet session
721         #
722         $pr->close;
723
724         #
725         #       Lets examine the userlist stored in @list
726         #
727         foreach(@list) {
728                 #
729                 #       We are interested in active sessions only
730                 #
731                 if (/Active/i) {
732                         ($port, $user) = split;
733                         #
734                         #       Strip out any prefix, suffix and
735                         #       realm from $user check to see if
736                         #       $ARGV[3] matches.
737                         #
738                         if($ARGV[3] eq strip_username($user)) {
739                                 print LOG "  User '$ARGV[3]' found on '$ARGV[1]:$port'.\n" if ($debug);
740                                 return 1;
741                         }
742                 }
743         }
744         print LOG "  User '$ARGV[3]' not found on '$ARGV[1]'.\n" if ($debug);
745         0;
746 }
747
748 #
749 #       Check a Patton 2800 via SNMP
750 #
751 #       Version: 1.0
752 #
753 #       Author: Antonio Dias of SST Internet <accdias@sst.com.br>
754 #
755 sub patton_snmp {
756    my($oid);
757
758    #$oid = '.1.3.6.1.4.1.1768.5.100.1.40.' . hex $ARGV[4];
759    # Reported by "Andria Legon" <andria@patton.com>
760    # The OID below should be the correct one instead of the one above.
761    $oid = '.1.3.6.1.4.1.1768.5.100.1.56.' . hex $ARGV[4];
762    #
763    # Check if the session still active
764    # 
765    if (snmpget($ARGV[1], "monitor", "$oid") == 0) {
766       print LOG "  Session $ARGV[4] still active on NAS " . 
767         "$ARGV[1], port $ARGV[2], for user $ARGV[3].\n" if ($debug);
768       return 1;
769    }
770    0;
771 }
772
773 #
774 #      Check a Digitro BXS via rusers
775 #
776 #      Version: 1.1
777 #
778 #      Author: Antonio Dias of SST Internet <accdias@sst.com.br>
779 #
780 sub digitro_rusers {
781    my ($ret);
782    local $_;
783
784    if (-e $rusers && -x $rusers) {
785       #
786       # Get a list of users logged in via rusers
787       #
788       $_ = `$rusers $ARGV[1]`;
789       $ret = ((/$ARGV[3]/) ? 1 : 0);
790    } else {
791       print LOG "   Error: can't execute $rusers\n" if $debug;
792       $ret = 2;
793    }
794    $ret;
795 }
796
797 #
798 #       Check Cyclades PR3000 and PR4000 via SNMP
799 #
800 #       Version: 1.0
801 #
802 #       Author: Antonio Dias of SST Internet <accdias@sst.com.br>
803 #
804 sub cyclades_snmp {
805    my ($oid, $ret);
806    local $_;
807    
808    $oid = ".1.3.6.1.4.1.2925.3.3.6.1.1.2";
809
810    if (-e $snmpwalk && -x $snmpwalk) {  
811       #
812       # Get a list of logged in users via snmpwalk
813       #
814       # I'm looking for a way to do this using SNMP_Session
815       # module. Any help would be great.
816       #
817       $_ = `$snmpwalk $ARGV[1] public $oid`;
818       $ret = ((/$ARGV[3]/) ? 1 : 0);
819    } else {
820       print LOG "   Error: can't execute $snmpwalk\n" if $debug;
821       $ret = 2;
822    }
823    $ret;
824 }
825
826 #
827 #       3Com/USR HiPer Arc Total Control.
828 #       This works with HiPer Arc 4.0.30
829 #       (this routine by Igor Brezac <igor@ipass.net>)
830
831 $usrm    = '.iso.org.dod.internet.private.enterprises.429';
832 sub usrhiper_snmp {
833         my ($login,$password,$oidext);
834
835         # Look up community string in naspasswd file.
836         ($login, $password) = naspasswd($ARGV[1], 1);
837         if ($login && $login ne 'SNMP') {
838                 if($debug) {
839                         print LOG
840                         "   Error: Need SNMP community string for $ARGV[1]\n";
841                 }
842                 return 2;
843         } else {
844                 $password = "public";
845         }
846
847         $oidext = 1257 + 256*int(($ARGV[2]-1) / $hiper_density) +
848                                 (($ARGV[2]-1) % $hiper_density);
849         my ($login);
850
851         $login = snmpget($ARGV[1], $password, "$usrm.4.10.1.1.18.$oidext");
852         if ($login =~ /\"/) {
853                 $login =~ /^.*\"([^"]+)\"/;
854                 $login = $1;
855         }
856
857         print LOG "  user at port S$ARGV[2]: $login\n" if ($debug);
858
859         ($login eq $ARGV[3]) ? 1 : 0;
860 }
861
862
863 #
864 #       Check USR Netserver with Telnet - based on tc_tccheck.
865 #       By "Marti" <mts@interplanet.es>
866 #
867 sub usrnet_telnet {
868         #
869         #       Localize all variables first.
870         #
871         my ($ts, $terminalserver, $login, $password);
872         my ($telnet, $curprompt, $curline, $ok);
873         my (@curlines, $user, $port);
874
875         return 2 unless (check_net_telnet());
876
877         $terminalserver = $ARGV[1];
878         $Port_seen = 0;
879         #
880         #       Get login name and password for a certain NAS from $naspass.
881         #
882         ($login, $password) = naspasswd($terminalserver, 1);
883         return 2 if ($password eq "");
884
885         #
886         #       Communicate with Netserver using Net::Telnet, then access
887         #       list connectionsto see who are logged in. 
888         # 
889         $telnet = new Net::Telnet (Timeout => 10,
890                                    Prompt => '/\>/');
891         $telnet->open($terminalserver);
892
893         #
894         #       Log on Netserver
895         #
896         $telnet->login($login, $password);
897
898         #
899         #     Launch list connections command
900
901         $telnet->print("list connections");
902
903         while ($curprompt ne "\>") {
904                 ($curline, $curprompt) = $telnet->waitfor
905                         ( String => "\>",
906                          Timeout => 10);
907                 $ok = $telnet->print("");
908                 push @curlines, split(/^/m, $curline);
909         }
910
911         $telnet->close;
912         #
913         #       Telnet closed.  We got the info.  Let's examine it.
914         #
915         foreach(@curlines) {
916                 if ( /mod\:/ ) {
917                         ($port, $user, $dummy) = split;
918                         #
919                         # Strip out any prefixes and suffixes 
920                         # from the username
921                         #
922                         # uncomment this if you use the standard
923                         # prefixes
924                         #$user =~ s/^[PSC]//;
925                         #$user =~ s/\.(ppp|slip|cslip)$//;
926                         #
927                         # Check to see if $user is already connected
928                         #
929                         if ($user eq $ARGV[3]) {
930                                 print LOG "  $user matches $ARGV[3] " .
931                                         "on port $port" if ($debug);
932                                 return 1;
933                         };
934                 };
935         };
936         print LOG 
937         "  $ARGV[3] not found on Netserver logged users list " if ($debug);
938         0;
939 }
940
941 #
942 #       Versanet's Perl Script Support:
943 #
944 #       ___ versanet_snmp 1.0 by support@versanetcomm.com ___ July 1999
945 #       Versanet Enterprise MIB Base: 1.3.6.1.4.1.2180
946 #   
947 #       VN2001/2002 use slot/port number to locate modems. To use snmp get we
948 #       have to translate the original port number into a slot/port pair.
949 #
950 $vsm     = '.iso.org.dod.internet.private.enterprises.2180';
951 sub versanet_snmp {
952         
953         print LOG "argv[2] = $ARGV[2] " if ($debug);
954         $port = $ARGV[2]%8;
955         $port = 8 if ($port eq 0);        
956         print LOG "port = $port " if ($debug);
957         $slot = (($ARGV[2]-$port)/8)+1;
958         print LOG "slot = $slot" if ($debug);
959         $loginname = snmpget($ARGV[1], "public", "$vsm.27.1.1.3.$slot.$port");
960 #
961 #       Note: the "public" string above could be replaced by the public
962 #             community string defined in Versanet VN2001/VN2002.
963 #
964           print LOG "  user at slot $slot port $port: $loginname\n" if ($debug);          ($loginname eq $ARGV[3]) ? 1 : 0;     
965 }
966
967
968 # 1999/08/24 Chris Shenton <chris@shenton.org>
969 # Check Bay8000 NAS (aka: Annex) using finger. 
970 # Returns from "finger @bay" like:
971 #   Port  What User         Location         When          Idle  Address
972 #   asy2  PPP  bill         ---              9:33am         :08  192.168.1.194
973 #   asy4  PPP  hillary      ---              9:36am         :04  192.168.1.195
974 #   [...]
975 # But also returns partial-match users if you say like "finger g@bay":
976 #   Port  What User         Location         When          Idle  Address
977 #   asy2  PPP  gore         ---              9:33am         :09  192.168.1.194
978 #   asy22 PPP  gwbush       ---              Mon  9:19am    :07  192.168.1.80
979 # So check exact match of username!
980
981 sub bay_finger {                # ARGV: 1=nas_ip, 2=nas_port, 3=login, 4=sessid
982     open(FINGER, "finger $ARGV[3]\@$ARGV[1]|") || return 2; # error
983     while(<FINGER>) {
984         my ($Asy, $PPP, $User) = split;
985         if( $User =~ /^$ARGV[3]$/ ){
986             close FINGER;
987             print LOG "checkrad:bay_finger: ONLINE $ARGV[3]\@$ARGV[1]"
988                 if ($debug);
989             return 1; # online
990         }
991     }
992     close FINGER;
993     print LOG "checkrad:bay_finger: offline $ARGV[3]\@$ARGV[1]" if ($debug);
994     return 0; # offline
995 }
996
997 ###############################################################################
998
999 if ($debug) {
1000         open(LOG, ">>$debug");
1001         $now = localtime;
1002         print LOG "$now checkrad @ARGV\n";
1003 }
1004
1005 if ($#ARGV != 4) {
1006         print LOG "Usage: checkrad nas_type nas_ip " .
1007                         "nas_port login session_id\n" if ($debug);
1008         print STDERR "Usage: checkrad nas_type nas_ip " .
1009                         "nas_port login session_id\n";
1010         close LOG if ($debug);
1011         exit(2);
1012 }
1013
1014 if ($ARGV[0] eq 'livingston') {
1015         $ret = &livingston_snmp;
1016 } elsif ($ARGV[0] eq 'cisco') {
1017         $ret = &cisco_snmp;
1018 } elsif ($ARGV[0] eq 'cvx') {
1019         $ret = &cvx_snmp;
1020 } elsif ($ARGV[0] eq 'multitech') {
1021         $ret = &multitech_snmp;
1022 } elsif ($ARGV[0] eq 'computone') {
1023         $ret = &computone_finger;
1024 } elsif ($ARGV[0] eq 'max40xx') {
1025         $ret = &max40xx_finger;
1026 } elsif ($ARGV[0] eq 'ascend' || $ARGV[0] eq 'max40xx_snmp') {
1027         $ret = &ascend_snmp;
1028 } elsif ($ARGV[0] eq 'portslave') {
1029         $ret = &portslave_finger;
1030 } elsif ($ARGV[0] eq 'tc') {
1031         $ret = &tc_tccheck;
1032 } elsif ($ARGV[0] eq 'pathras') {
1033         $ret = &cyclades_telnet;
1034 } elsif ($ARGV[0] eq 'pr3000') {
1035         $ret = &cyclades_snmp;
1036 } elsif ($ARGV[0] eq 'pr4000') {
1037         $ret = &cyclades_snmp;
1038 } elsif ($ARGV[0] eq 'patton') {
1039         $ret = &patton_snmp;
1040 } elsif ($ARGV[0] eq 'digitro') {
1041         $ret = &digitro_rusers;
1042 } elsif ($ARGV[0] eq 'usrhiper') {
1043         $ret = &usrhiper_snmp;
1044 } elsif ($ARGV[0] eq 'netserver') {
1045         $ret = &usrnet_telnet;
1046 } elsif ($ARGV[0] eq 'versanet') {
1047         $ret = &versanet_snmp;
1048 } elsif ($ARGV[0] eq 'bay') {
1049         $ret = &bay_finger;
1050 } elsif ($ARGV[0] eq 'other') {
1051         $ret = 1;
1052 } else {
1053         print LOG "  checkrad: unknown NAS type $ARGV[0]\n" if ($debug);
1054         print STDERR "checkrad: unknown NAS type $ARGV[0]\n";
1055         $ret = 2;
1056 }
1057
1058 if ($debug) {
1059         $mn = "login ok";
1060         $mn = "double detected" if ($ret == 1);
1061         $mn = "error detected" if ($ret == 2);
1062         print LOG "  Returning $ret ($mn)\n";
1063         close LOG;
1064 }
1065
1066 exit($ret);