1#! @EXPECT_PATH@ -- 2## 3## @PACKAGE@ @VERSION@ 4@copyright@ 5# 6# flogin - foundry login 7# 8# Most options are intuitive for logging into a foundry switch. 9# this should be the clogin, but foundry can't seem to dislodge 10# their heads and make the UI consistent. i think the UI 11# development has been outsourced to Fisher Price. 12# The default is to enable (thus -noenable). Some folks have 13# setup tacacs to have a user login at priv-lvl = 15 (enabled) 14# so the -autoenable flag was added for this case (don't go through 15# the process of enabling and the prompt will be the "#" prompt. 16# The default username password is the same as the vty password. 17# 18 19# Sometimes routers take awhile to answer (the default is 10 sec) 20set timeoutdflt 45 21# Some CLIs having problems if we write too fast (Extreme, PIX, Cat) 22set send_human {.2 .1 .4 .2 1} 23 24@login_top@ 25 26# Log into the router. 27# returns: 0 on success, 1 on failure 28proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } { 29 global spawn_id in_proc do_command do_script passphrase 30 global prompt u_prompt p_prompt e_prompt sshcmd telnetcmd 31 set in_proc 1 32 set uprompt_seen 0 33 34 # try each of the connection methods in $cmethod until one is successful 35 set progs [llength $cmethod] 36 foreach prog [lrange $cmethod 0 end] { 37 incr progs -1 38 if [string match "telnet*" $prog] { 39 regexp {telnet(:([^[:space:]]+))*} $prog command suffix port 40 if {"$port" == ""} { 41 set retval [ catch {eval spawn [split "$telnetcmd $router"]} reason ] 42 } else { 43 set retval [ catch {eval spawn [split "$telnetcmd $router $port"]} reason ] 44 } 45 if { $retval } { 46 send_user "\nError: telnet failed: $reason\n" 47 return 1 48 } 49 } elseif [string match "ssh*" $prog] { 50 # ssh to the router & try to login with or without an identfile. 51 regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port 52 set cmd $sshcmd 53 if {"$port" != ""} { 54 set cmd "$cmd -p $port" 55 } 56 if {"$cyphertype" != ""} { 57 set cmd "$cmd -c $cyphertype" 58 } 59 if {"$identfile" != ""} { 60 set cmd "$cmd -i $identfile" 61 } 62 set retval [ catch {eval spawn [split "$cmd -x -l $user $router" { }]} reason ] 63 if { $retval } { 64 send_user "\nError: $cmd failed: $reason\n" 65 return 1 66 } 67 } elseif ![string compare $prog "rsh"] { 68 send_error "\nError: unsupported method: rsh\n" 69 if { $progs == 0 } { 70 return 1 71 } 72 continue; 73 } else { 74 send_user "\nError: unknown connection method: $prog\n" 75 return 1 76 } 77 sleep 0.3 78 79 # This helps cleanup each expect clause. 80 expect_after { 81 timeout { 82 global in_proc 83 send_user "\nError: TIMEOUT reached\n" 84 catch {close}; catch {wait}; 85 if {$in_proc} { 86 return 1 87 } else { 88 continue 89 } 90 } eof { 91 global in_proc 92 send_user "\nError: EOF received\n" 93 catch {close}; catch {wait}; 94 if {$in_proc} { 95 return 1 96 } else { 97 continue 98 } 99 } 100 } 101 102 # Here we get a little tricky. There are several possibilities: 103 # the router can ask for a username and passwd and then 104 # talk to the TACACS server to authenticate you, or if the 105 # TACACS server is not working, then it will use the enable 106 # passwd. Or, the router might not have TACACS turned on, 107 # then it will just send the passwd. 108 # if telnet fails with connection refused, try ssh 109 expect { 110 -re "(Connection refused|Secure connection \[^\n\r]+ refused)" { 111 catch {close}; catch {wait}; 112 if !$progs { 113 send_user "\nError: Connection Refused ($prog): $router\n" 114 return 1 115 } 116 } 117 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { 118 catch {close}; catch {wait}; 119 if !$progs { 120 send_user "\nError: Connection closed ($prog): $router\n" 121 return 1 122 } 123 } 124 -re "Telnet server disabled" { 125 catch {close}; catch {wait}; 126 if !$progs { 127 send_user "\nError: Connection Refused ($prog): $router\n" 128 return 1 129 } 130 } 131 eof { send_user "\nError: Couldn't login\n"; wait; return 1 } 132 -nocase "unknown host\r" { 133 catch {close}; catch {wait}; 134 send_user "\nError: Unknown host $router\n"; wait; return 1 135 } 136 "Host is unreachable" { 137 catch {close}; catch {wait}; 138 send_user "\nError: Host Unreachable: $router\n"; wait; return 1 139 } 140 "No address associated with name" { 141 catch {close}; catch {wait}; 142 send_user "\nError: Unknown host $router\n"; wait; return 1 143 } 144 -re "(Host key not found |The authenticity of host .* be established)" { 145 expect { 146 -re "\\(yes\/no\[^\\)]*\\)\\?" { 147 send "yes\r"; 148 send_user "\nHost $router added to the list of known hosts.\n" 149 } 150 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 151 } 152 exp_continue 153 } 154 -re "HOST IDENTIFICATION HAS CHANGED" { 155 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" 156 expect { 157 -re "\\(yes\/no\\)\\?" { send "no\r" } 158 -re " strict checking\.\[\r\n]+" { } 159 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 160 } 161 catch {close}; catch {wait}; 162 return 1 163 } 164 -re "Offending key for " { 165 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" 166 expect { 167 -re "\\(yes\/no\\)\\?" { send "no\r" } 168 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 169 } 170 catch {close}; catch {wait}; 171 return 1 172 } 173 -nocase -re "^warning: remote host denied authentication agent forwarding." { 174 exp_continue; 175 } 176 -re "(denied|Sorry)" { 177 send_user "\nError: Check your passwd for $router\n" 178 catch {close}; catch {wait}; return 1 179 } 180 "Login failed" { 181 send_user "\nError: Check your passwd for $router\n" 182 return 1 183 } 184 -re "% (Bad passwords|Authentication failed)" { 185 send_user "\nError: Check your passwd for $router\n" 186 return 1 187 } 188 -re "@\[^\r\n]+ $p_prompt" { 189 # ssh pwd prompt 190 send -h -- "$userpswd\r" 191 exp_continue 192 } 193 -re "Enter passphrase.*: " { 194 # sleep briefly to allow time for stty -echo 195 sleep .3 196 send -- "$passphrase\r" 197 exp_continue 198 } 199 -re "$u_prompt" { 200 send -h -- "$user\r" 201 set uprompt_seen 1 202 exp_continue 203 } 204 -re "$p_prompt" { 205 if {$uprompt_seen == 1} { 206 send -h -- "$userpswd\r" 207 } else { 208 send -h -- "$passwd\r" 209 } 210 exp_continue 211 } 212 "$prompt" { break; } 213 "Login invalid" { 214 send_user "\nError: Invalid login: $router\n"; 215 catch {close}; catch {wait}; return 1 216 } 217 } 218 } 219 220 set in_proc 0 221 return 0 222} 223 224# Enable 225proc do_enable { enauser enapasswd } { 226 global prompt in_proc 227 global u_prompt e_prompt 228 set in_proc 1 229 230 send -h "enable\r" 231 expect { 232 -re "$u_prompt" { send -h -- "$enauser\r"; exp_continue} 233 -re "$e_prompt" { send -h -- "$enapasswd\r"; exp_continue} 234 "#" { set prompt "#" } 235 "(enable)" { set prompt "> (enable) " } 236 denied { send_user "\nError: Check your Enable passwd\n"; 237 return 1 238 } 239 "Incorrect username or password" { 240 send_user "\nError: Check your Enable passwd\n"; 241 return 1 242 } 243 "% Bad passwords" { send_user "\nError: Check your Enable passwd\n"; 244 return 1 245 } 246 } 247 # We set the prompt variable (above) so script files don't need 248 # to know what it is. 249 set in_proc 0 250 return 0 251} 252 253# Run commands given on the command line. 254proc run_commands { prompt command } { 255 global do_interact do_saveconfig in_proc 256 set in_proc 1 257 258 send -h "skip-page-display\r" 259 expect -re "$prompt" {} 260 # If logging in as an unpriv user (i.e., level 5 read-only), then 261 # "skip-page-display" isn't available, unless you also configure: 262 # privilege exec level 5 skip-page-display 263 # 264 # On some platforms, like the MLX, you can work around that 265 # with this: 266 send "terminal length 0\r" 267 expect -re "$prompt" {} 268 269 # match foundry config mode prompts too, such as ssh@router(config-if)# 270 # or ssh@truncatedr(config-if)#. NOTE that, at least, as of v6.0.0 271 # (2018?), Foundry NetIron does not truncate, but this shouldnt hurt. 272 regsub -lineanchor -- {^(.{1,11}).*([#>])$} $prompt {\1} reprompt 273 regsub -all -- {[\\]$} $reprompt {} reprompt 274 append reprompt {([^#>\r\n]+)?[#>]([\\r\\n]+)?} 275 276 # handle escaped ;s in commands, and ;; and ^; 277 regsub -all {([^\\]);} $command "\\1\u0002;" esccommand 278 regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command 279 regsub {^;} $command "\u0002;" esccommand 280 regsub -all {[\\];} $esccommand ";" command 281 regsub -all {\u0002;} $command "\u0002" esccommand 282 set sep "\u0002" 283 set commands [split $esccommand $sep] 284 set num_commands [llength $commands] 285 for {set i 0} {$i < $num_commands} { incr i} { 286 send -h -- "[subst -nocommands [lindex $commands $i]]\r" 287 expect { 288 -re " *\b+" { exp_continue } 289 -re "^\[^\n\r]*$reprompt." { exp_continue } 290 -re "^\[^\n\r *]*$reprompt" {} 291 -re "\[\n\r]" { exp_continue } 292 -- "---More---" { # EdgeIron cant disable pager 293 send " " 294 exp_continue 295 } 296 } 297 } 298 299 if { $do_interact == 1 } { 300 interact 301 return 0 302 } 303 304 send -h "exit\r" 305 expect { 306 "\n" { exp_continue } 307 -re "^\[^ ]+>" { send -h "exit\r" 308 exp_continue 309 } 310 timeout { catch {close}; catch {wait}; 311 return 0 312 } 313 eof { return 0 } 314 -re "Configuration modified, save\? \[\r\n]*" { 315 if {$do_saveconfig} { 316 catch {send "y\r"} 317 } else { 318 catch {send "n\r"} 319 } 320 exp_continue 321 } 322 } 323 set in_proc 0 324} 325 326# 327# For each router... (this is main loop) 328# 329source_password_file $password_file 330set in_proc 0 331set exitval 0 332foreach router [lrange $argv $i end] { 333 set router [string tolower $router] 334 send_user "$router\n" 335 336 # device timeout 337 set timeout [find timeout $router] 338 if { [llength $timeout] == 0 } { 339 set timeout $timeoutdflt 340 } 341 342 # Figure out prompt. 343 # Since autoenable is off by default, if we have it defined, it 344 # was done on the command line. If it is not specifically set on the 345 # command line, check the password file. 346 if $avautoenable { 347 set autoenable 1 348 set enable 0 349 set prompt "#" 350 } else { 351 set ae [find autoenable $router] 352 if { "$ae" == "1" } { 353 set autoenable 1 354 set enable 0 355 set prompt "#" 356 } else { 357 set autoenable 0 358 set enable $avenable 359 set prompt ">" 360 } 361 } 362 363 # Figure out passwords 364 if { $do_passwd || $do_enapasswd } { 365 set pswd [find password $router] 366 if { [llength $pswd] == 0 } { 367 send_user -- "\nError: no password for $router in $password_file.\n" 368 continue 369 } 370 if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } { 371 send_user -- "\nError: no enable password for $router in $password_file.\n" 372 continue 373 } 374 if { $do_passwd } { 375 set passwd [join [lindex $pswd 0] ""] 376 } else { 377 set passwd $userpasswd 378 } 379 if { $do_enapasswd } { 380 set enapasswd [join [lindex $pswd 1] ""] 381 } else { 382 set enapasswd $enapasswd 383 } 384 } else { 385 set passwd $userpasswd 386 set enapasswd $enapasswd 387 } 388 389 # Figure out username 390 if {[info exists username]} { 391 # command line username 392 set ruser $username 393 } else { 394 set ruser [join [find user $router] ""] 395 if { "$ruser" == "" } { set ruser $default_user } 396 } 397 398 # Figure out username's password (if different from the vty password) 399 if {[info exists userpasswd]} { 400 # command line username 401 set userpswd $userpasswd 402 } else { 403 set userpswd [join [find userpassword $router] ""] 404 if { "$userpswd" == "" } { set userpswd $passwd } 405 } 406 407 # Figure out enable username 408 if {[info exists enausername]} { 409 # command line enausername 410 set enauser $enausername 411 } else { 412 set enauser [join [find enauser $router] ""] 413 if { "$enauser" == "" } { set enauser $ruser } 414 } 415 416 # Figure out prompts 417 set u_prompt [find userprompt $router] 418 if { "$u_prompt" == "" } { 419 set u_prompt "(Username|login|Name|User Name) *:" 420 } else { 421 set u_prompt [join [lindex $u_prompt 0] ""] 422 } 423 set p_prompt [find passprompt $router] 424 if { "$p_prompt" == "" } { 425 set p_prompt "(\[Pp]assword):" 426 } else { 427 set p_prompt [join [lindex $p_prompt 0] ""] 428 } 429 set e_prompt [find enableprompt $router] 430 if { "$e_prompt" == "" } { 431 set e_prompt "\[Pp]assword:" 432 } else { 433 set e_prompt [join [lindex $e_prompt 0] ""] 434 } 435 436 # Figure out identity file to use 437 set identfile [join [lindex [find identity $router] 0] ""] 438 439 # Figure out passphrase to use 440 if {[info exists avpassphrase]} { 441 set passphrase $avpassphrase 442 } else { 443 set passphrase [join [lindex [find passphrase $router] 0] ""] 444 } 445 if { ! [string length "$passphrase"]} { 446 set passphrase $passwd 447 } 448 449 # Figure out cypher tpye 450 if {[info exists cypher]} { 451 # command line cypher type 452 set cyphertype $cypher 453 } else { 454 set cyphertype [find cyphertype $router] 455 } 456 457 # Figure out connection method 458 set cmethod [find method $router] 459 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } 460 461 # Figure out the SSH executable name 462 set sshcmd [join [lindex [find sshcmd $router] 0] ""] 463 if { "$sshcmd" == "" } { set sshcmd {ssh} } 464 465 # Figure out the telnet executable name 466 set telnetcmd [join [lindex [find telnetcmd $router] 0] ""] 467 if { "$telnetcmd" == "" } { set telnetcmd "@TELNET_CMD@" } 468 469 # if [-mM], skip do not login 470 if { $do_cloginrcdbg > 0 } { continue; } 471 472 # Login to the router 473 if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile ]} { 474 incr exitval 475 continue 476 } 477 if { $enable } { 478 if {[do_enable $enauser $enapasswd]} { 479 if { $do_command || $do_script } { 480 incr exitval 481 catch {close}; catch {wait}; 482 continue 483 } 484 } 485 } 486 # we are logged in, now figure out the full prompt 487 send -h "\r" 488 expect { 489 -re "\[\r\n]+" { exp_continue; } 490 -re "^.+$prompt" { set junk $expect_out(0,string); 491 regsub -all "\[\]\[ ]" $junk {\\&} prompt; 492 } 493 } 494 495 496 if { $do_command } { 497 if {[run_commands $prompt $command]} { 498 incr exitval 499 continue 500 } 501 } elseif { $do_script } { 502 # fucking foundry 503 send -h "skip-page-display\r" 504 expect -re $prompt {} 505 # If logging in as an unpriv user (i.e., level 5 read-only), then 506 # "skip-page-display" isn't available, unless you also configure: 507 # privilege exec level 5 skip-page-display 508 # 509 # On some platforms, like the MLX, you can work around that 510 # with this: 511 send "terminal length 0\r" 512 expect -re "$prompt" {} 513 source $sfile 514 catch {close}; 515 } else { 516 label $router 517 log_user 1 518 interact 519 } 520 521 # End of for each router 522 catch {wait}; 523 sleep 0.3 524} 525exit $exitval 526