1#!/usr/bin/perl 2# 3# login program to invoke PPP. 4# RADIUS accounting is NOT handled by this; it is handled by /etc/ppp/ 5# ip-up and ip-down which are invoked when the TCP/IP connection is up. 6 7# version 0.1 November 5 1996 8# clean up the code, minor features. 9 10# version 0.02 May 8 1996 11# 12# start implementing other types of logins, not only Framed. 13# Also honor static IP addresses. 14# 15# version 0.01 April 1 1996 16# 17# - ignore RADIUS server requests for Framed-User, just 18# do PPP. Later, this should be honored. For now, 19# just use RADIUS for authentication; it's much simpler. 20# Always use dynamic addresses. 21# 22 23use strict; 24use GDBM_File; 25 26#### CONFIGURATION SECTION ################################################## 27 28# Local IP address for the PPP connection. 29my $ip_address_local = "203.176.0.3"; 30 31# First IP address for this terminal server, if dynamic addressing 32# is requested, or if nothing is specified for Framed-IP-Address. 33my $ip_address_begin = "203.176.0.161"; 34 35# IP translation factor; subtract this value from radclient before adding 36# the beginning IP address. 37my $ip_translate_factor = 32; 38 39# Debugging to screen? 40my $debug = 1; 41 42# PPP parameters: 43 44# Async map - this one escapes only XON and XOFF characters. 45my $asyncmap = "0x000A0000"; 46 47# MTU and MRU. 296 is good for interactive performance, 48# but larger ones will lead to less overhead for file transfers. 49# Maximum is 1500. 50my ($mtu, $mru) = (296, 296); 51 52# If we're using proxy ARP, set this to "proxyarp", else leave it blank. 53# my $proxyarp = "proxyarp"; 54my $proxyarp = ""; 55 56# Login host for non-framed connections. 57# This should only be an IP address, since that's what 58# Login-IP-Host should be. 59my $login_host = "203.176.0.4"; # marikit.iphil.net 60 61# Programs and files. 62my $prog_pppd = "/usr/sbin/pppd"; 63my $prog_radacct = "/usr/local/lib/radiusclient/radacct"; 64my $prog_rlogin = "/usr/bin/rlogin"; 65my $prog_telnet = "/bin/telnet"; 66my $prog_tcpclear = "/bin/telnet -e ''"; 67my $prog_tty = "/usr/bin/tty"; 68my $prog_who = "/usr/bin/who"; 69 70my $path_portinfo = "/var/ipoint/acct/portinfo"; 71my $path_radiusclient_map = "/etc/radclient/port-id-map"; 72 73############################################################################# 74 75# Main program. 76 77print "Starting.\n" if ($debug); 78 79# Run 'who am i' to determine the current port. 80my $port = `$prog_tty`; 81chomp ($port); 82 83# Translate port numbers to numbers for RADIUS. 84# This translation is done again by radacct, but it may be useful here. 85# Remove if CPU time is a problem. 86 87my ($portid, $line); 88open (H, $path_radiusclient_map); 89while (($line = <H>) && (!$portid)) 90{ 91 my @info = split (/\s+/, $line); 92 $portid = $info[1] if ($info[0] eq $port); 93} 94close (H); 95 96if ($debug) 97{ 98 # Print out all the RADIUS variables. 99 my @el = grep (/^RADIUS/, keys (%ENV)); 100 my $e; 101 foreach $e (@el) 102 { 103 print "$e = " . $ENV{$e} . "\n"; 104 } 105} 106 107# If the service type is Framed, then give them PPP. 108# SLIP is not implemented (and will probably never be). 109 110my $username = $ENV{"RADIUS_USER_NAME"}; 111 112# Generate a "unique" string for the session ID. 113my $sessionid = "$$" . time (); 114 115if ($ENV{"RADIUS_SERVICE_TYPE"} =~ /^Framed$/) 116{ 117 118# Use the specified IP address, or generate one if none is specified, 119# or a dynamic one requested. Or, let the user negotiate the address. 120 121 my $ip_address = $ENV{"RADIUS_FRAMED_IP_ADDRESS"}; 122 123 if (!$ip_address || ($ip_address eq "255.255.255.254")) 124 { 125 my @ipn = split (/\./, $ip_address_begin); 126 $ipn[3] += $portid - $ip_translate_factor; 127 $ip_address = join ('.', @ipn); 128 129 if ($debug) 130 { 131 print "port: $port\n"; 132 print "portid: $portid\n"; 133 print "ip_translate_factor: $ip_translate_factor\n"; 134 print "ip_address: $ip_address\n"; 135 print "mru: $mru\n"; 136 } 137 138 } 139 elsif ($ip_address eq "255.255.255.255") 140 { 141 # Clear it out so that pppd will let the remote end specify the 142 # IP address. 143 $ip_address = ""; 144 } 145 146 # Override the specified MTU. 147 $mtu = $ENV{"RADIUS_FRAMED_MTU"} if $ENV{"RADIUS_FRAMED_MTU"}; 148 149 # If no compression is specified, turn it off. 150 my $compress; 151 if (!$ENV{"RADIUS_FRAMED_COMPRESSION"}) 152 { 153 $compress = "-vj"; 154 } 155 156# Fix up the parameters to be passed to ip-up. Include Framed-Route. 157# Escape spaces with %20's. 158 159 # Split up the framed route into multiple parts. 160 # Separate the different given routes with bars. 161 my $routelist = join ("@", map {$ENV{$_}} 162 grep {/^RADIUS_FRAMED_ROUTE/} keys (%ENV) 163 ); 164 $routelist =~ s/ /%20/g; 165 166 my $param = join (':', $sessionid, $username, $port, $portid, 167 $ENV{"RADIUS_SESSION_TIMEOUT"}, $routelist); 168 169 170# Run pppd through exec, so that it grabs hold of the terminal 171# and catches disconnections. 172 173 # Portmaster-style prompt. 174 if ($ENV{"RADIUS_SESSION_TIMEOUT"}) 175 { 176 print "Session timeout: " . $ENV{"RADIUS_SESSION_TIMEOUT"} . 177 " seconds.\n"; 178 } 179 print "PPP session from ($ip_address_local) to $ip_address beginning...."; 180 my $pppdcmd = 181 "$prog_pppd $ip_address_local:$ip_address modem crtscts " . 182 "asyncmap $asyncmap lock -detach $compress " . 183 "ipparam $param mtu $mtu mru $mru $proxyarp"; 184 185 exec ($pppdcmd); 186} 187elsif ($ENV{"RADIUS_SERVICE_TYPE"} =~ /Login/) 188{ 189 # Warning: This code has not been tested as well as the PPP version, 190 # as of now (19961107). 191 192 # Determine what host to connect to. 193 if (($ENV{"RADIUS_LOGIN_IP_HOST"} eq "0.0.0.0") || 194 !defined ($ENV{"RADIUS_LOGIN_IP_HOST"})) 195 { 196 # Do nothing, it's already specified above in the config section. 197 } 198 elsif ($ENV{"RADIUS_LOGIN_IP_HOST"} eq "255.255.255.255") 199 { 200 # The user should be able to choose. Prompt the user. 201 print "Host to connect to? "; 202 $login_host = <STDIN>; 203 chomp ($login_host); 204 } 205 else 206 { 207 # Use what's specified by the RADIUS server. 208 $login_host = $ENV{"RADIUS_LOGIN_IP_HOST"}; 209 } 210 211 # Log into a host. Default to telnet. Do the accounting 212 # now, since the target of the login wouldn't know how to 213 # account for it. 214 215 # Start accounting. Send the record. 216 open (H, "| $prog_radacct") || die ("Cannot run $prog_radacct"); 217 218 my $login_service = $ENV{"RADIUS_LOGIN_SERVICE"}; 219 220 my $cmd = 221 "Acct-Session-ID = \"$sessionid\"\n" . 222 "User-Name = \"$username\"\n" . 223 "Acct-Status-Type = Start\n" . 224 "Acct-Authentic = RADIUS\n" . 225 "Service-Type = Login\n" . 226 "Login-Service = " . $login_service . "\n" . 227 "Login-IP-Host = $login_host\n"; 228 229 print H $cmd; 230 close (H); 231 232 # Time. 233 my $timestart = time (); 234 235 # What protocol are we running? 236 my ($prog_run, $login_port); 237 238 if ($login_service eq "Rlogin") 239 { 240 $prog_run = $prog_rlogin; 241 } 242 elsif ($login_service eq "Telnet") 243 { 244 $prog_run = $prog_telnet; 245 $login_port = $ENV{"RADIUS_LOGIN_PORT"}; 246 } 247 elsif ($login_service eq "TCP-Clear") 248 { 249 $prog_run = $prog_tcpclear; 250 $login_port = $ENV{"RADIUS_LOGIN_PORT"}; 251 } 252 253 # Store the user information into portinfo. We need to 254 # manually fork, since we have to know the PID of the program. 255 256 my $pid = fork (); 257 if ($pid == 0) 258 { 259 # Child. Run the program. 260 # print "Connecting to $login_host:\n"; 261 my $cmd = "$prog_run $login_host $login_port"; 262 exec ("$cmd"); 263 } 264 else 265 { 266 # Parent. 267 # Create the portinfo record, which needs the pid of the program 268 # to kill. 269 # The IP address is all zero, as it is not applicable here. 270 # Store the time now, and the Session-Timeout. 271 272 my %db_portinfo; 273 274 tie (%db_portinfo, "GDBM_File", $path_portinfo, GDBM_WRCREAT, 0600); 275 $db_portinfo{$portid} = 276 join (':', $username, "Login/$login_service", 277 "0.0.0.0", $pid, $timestart, $ENV{"RADIUS_SESSION_TIMEOUT"}); 278 untie (%db_portinfo); 279 280 # Wait for the session to finish. 281 waitpid ($pid, 0); 282 } 283 # Stop. Send the record. 284 open (H, "| $prog_radacct") || die ("Cannot run $prog_radacct"); 285 286 my $timespent = time () - $timestart; 287 288 my $cmd = 289 "Acct-Session-ID = \"$sessionid\"\n" . 290 "User-Name = \"$username\"\n" . 291 "Acct-Status-Type = Stop\n" . 292 "Acct-Authentic = RADIUS\n" . 293 "Service-Type = Login\n" . 294 "Login-Service = " . $login_service . "\n" . 295 "Login-IP-Host = $login_host\n" . 296 "Acct-Session-Time = $timespent\n"; 297 298 print H $cmd; 299 close (H); 300 301 # Remove the record from portinfo. 302 my %db_portinfo; 303 tie (%db_portinfo, "GDBM_File", $path_portinfo, GDBM_WRCREAT, 0600); 304 delete $db_portinfo{$portid}; 305 untie (%db_portinfo); 306} 307 308### END #### 309 310 311 312 313