1#! @EXPECT_PATH@ -- 2## 3## @PACKAGE@ @VERSION@ 4@copyright@ 5# 6# rblogin - Riverbed Steelhead login; this is starting from clogin r3115, with 7# a few changes for UI bugs. 8# 9# Most options are intuitive for logging into a Cisco router. 10# The default is to enable (thus -noenable). Some folks have 11# setup tacacs to have a user login at priv-lvl = 15 (enabled) 12# so the -autoenable flag was added for this case (don't go through 13# the process of enabling and the prompt will be the "#" prompt. 14# The default username password is the same as the vty password. 15# 16 17# Sometimes routers take awhile to answer (the default is 10 sec) 18set timeoutdflt 45 19# Some CLIs having problems if we write too fast (Extreme, PIX, Cat) 20set send_human {.2 .1 .4 .2 1} 21 22@login_top@ 23 24# Log into the router. 25# returns: 0 on success, 1 on failure, -1 if rsh was used successfully 26proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } { 27 global command spawn_id in_proc do_command do_script platform passphrase 28 global prompt prompt_match u_prompt p_prompt e_prompt sshcmd telnetcmd 29 set in_proc 1 30 set uprompt_seen 0 31 32 # try each of the connection methods in $cmethod until one is successful 33 set progs [llength $cmethod] 34 foreach prog [lrange $cmethod 0 end] { 35 incr progs -1 36 if [string match "telnet*" $prog] { 37 regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port 38 if {"$port" == ""} { 39 set retval [catch {eval spawn [split "$telnetcmd $router"]} reason] 40 } else { 41 set retval [catch {eval spawn [split "$telnetcmd $router $port"]} reason] 42 } 43 if { $retval } { 44 send_user "\nError: telnet failed: $reason\n" 45 return 1 46 } 47 } elseif [string match "ssh*" $prog] { 48 # ssh to the router & try to login with or without an identfile. 49 regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port 50 set cmd $sshcmd 51 if {"$port" != ""} { 52 set cmd "$cmd -p $port" 53 } 54 if {"$cyphertype" != ""} { 55 set cmd "$cmd -c $cyphertype" 56 } 57 if {"$identfile" != ""} { 58 set cmd "$cmd -i $identfile" 59 } 60 set retval [catch {eval spawn [split "$cmd -x -l $user $router" { }]} reason] 61 if { $retval } { 62 send_user "\nError: $cmd failed: $reason\n" 63 return 1 64 } 65 } elseif ![string compare $prog "rsh"] { 66 if { ! $do_command } { 67 if { [llength $cmethod] == 1 } { 68 send_user "\nError: rsh is an invalid method for -x and " 69 send_user "interactive logins\n" 70 } 71 if { $progs == 0 } { 72 return 1 73 } 74 continue; 75 } 76 77 # handle escaped ;s in commands, and ;; and ^; 78 regsub -all {([^\\]);} $command "\\1\u0002;" esccommand 79 regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command 80 regsub {^;} $command "\u0002;" esccommand 81 regsub -all {[\\];} $esccommand ";" command 82 regsub -all {\u0002;} $command "\u0002" esccommand 83 set sep "\u0002" 84 set commands [split $esccommand $sep] 85 set num_commands [llength $commands] 86 set rshfail 0 87 for {set i 0} {$i < $num_commands && !$rshfail} { incr i} { 88 log_user 0 89 set retval [catch {spawn rsh $user@$router [lindex $commands $i] } reason] 90 if { $retval } { 91 send_user "\nError: rsh failed: $reason\n" 92 log_user 1; return 1 93 } 94 send_user "$router# [lindex $commands $i]\n" 95 96 # rcmd does not get a pager and no prompts, so we just have to 97 # look for failures & lines. 98 expect { 99 "Connection refused" { catch {close}; catch {wait}; 100 send_user "\nError: Connection\ 101 Refused ($prog): $router\n" 102 set rshfail 1 103 } 104 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { 105 catch {close}; catch {wait}; 106 send_user "\nError: Connection\ 107 closed ($prog): $router\n" 108 set rshfail 1 109 } 110 "Host is unreachable" { catch {close}; catch {wait}; 111 send_user "\nError: Host Unreachable:\ 112 $router\n" 113 set rshfail 1 114 } 115 "No address associated with" { 116 catch {close}; catch {wait}; 117 send_user "\nError: Unknown host\ 118 $router\n" 119 set rshfail 1 120 } 121 -re "\b+" { exp_continue } 122 -re "\[\n\r]+" { send_user -- "$expect_out(buffer)" 123 exp_continue 124 } 125 timeout { catch {close}; catch {wait}; 126 send_user "\nError: TIMEOUT reached\n" 127 set rshfail 1 128 } 129 eof { catch {close}; catch {wait}; } 130 } 131 log_user 1 132 } 133 if { $rshfail } { 134 if { !$progs } { 135 return 1 136 } else { 137 continue 138 } 139 } 140 # fake the end of the session for rancid. 141 send_user "$router# exit\n" 142 # return rsh "success" 143 return -1 144 } else { 145 send_user "\nError: unknown connection method: $prog\n" 146 return 1 147 } 148 sleep 0.3 149 150 # This helps cleanup each expect clause. 151 expect_after { 152 timeout { 153 global in_proc 154 send_user "\nError: TIMEOUT reached\n" 155 catch {close}; catch {wait}; 156 if {$in_proc} { 157 return 1 158 } else { 159 continue 160 } 161 } eof { 162 global in_proc 163 send_user "\nError: EOF received\n" 164 catch {close}; catch {wait}; 165 if {$in_proc} { 166 return 1 167 } else { 168 continue 169 } 170 } 171 } 172 173 # Here we get a little tricky. There are several possibilities: 174 # the router can ask for a username and passwd and then 175 # talk to the TACACS server to authenticate you, or if the 176 # TACACS server is not working, then it will use the enable 177 # passwd. Or, the router might not have TACACS turned on, 178 # then it will just send the passwd. 179 # if telnet fails with connection refused, try ssh 180 expect { 181 -re "^<-+ More -+>\[^\n\r]*" { 182 # ASA will use the pager for long banners 183 send " "; 184 exp_continue 185 } 186 -re "(Connection refused|Secure connection \[^\n\r]+ refused)" { 187 catch {close}; catch {wait}; 188 if !$progs { 189 send_user "\nError: Connection Refused ($prog): $router\n" 190 return 1 191 } 192 } 193 -re "(Connection closed by|Connection to \[^\n\r]+ closed)" { 194 catch {close}; catch {wait}; 195 if !$progs { 196 send_user "\nError: Connection closed ($prog): $router\n" 197 return 1 198 } 199 } 200 eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 } 201 -nocase "unknown host\r" { 202 send_user "\nError: Unknown host $router\n"; 203 catch {close}; catch {wait}; 204 return 1 205 } 206 "Host is unreachable" { 207 send_user "\nError: Host Unreachable: $router\n"; 208 catch {close}; catch {wait}; 209 return 1 210 } 211 "No address associated with name" { 212 send_user "\nError: Unknown host $router\n"; 213 catch {close}; catch {wait}; 214 return 1 215 } 216 -re "(Host key not found |The authenticity of host .* be established)" { 217 expect { 218 -re "\\(yes\/no\[^\\)]*\\)\\?" { 219 send "yes\r"; 220 send_user "\nHost $router added to the list of known hosts.\n" 221 } 222 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 223 } 224 exp_continue 225 } 226 -re "HOST IDENTIFICATION HAS CHANGED" { 227 send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" 228 expect { 229 -re "\\(yes\/no\\)\\?" { send "no\r" } 230 -re " strict checking\.\[\r\n]+" { } 231 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 232 } 233 catch {close}; catch {wait}; 234 return 1 235 } 236 -re "Offending key for " { 237 send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" 238 expect { 239 -re "\\(yes\/no\\)\\?" { send "no\r" } 240 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 241 } 242 catch {close}; catch {wait}; 243 return 1 244 } 245 -nocase -re "^warning: remote host denied authentication agent forwarding." { 246 exp_continue; 247 } 248 -re "(denied|Sorry)" { 249 send_user "\nError: Check your passwd for $router\n" 250 catch {close}; catch {wait}; return 1 251 } 252 "Login failed" { 253 send_user "\nError: Check your passwd for $router\n" 254 catch {close}; catch {wait}; return 1 255 } 256 -re "% (Bad passwords|Authentication failed)" { 257 send_user "\nError: Check your passwd for $router\n" 258 catch {close}; catch {wait}; return 1 259 } 260 "Press any key to continue" { 261 # send_user "Pressing the ANY key\n" 262 send "\r" 263 exp_continue 264 } 265 -re "Enter Selection: " { 266 # Catalyst 1900s have some lame menu. Enter 267 # K to reach a command-line. 268 send "K\r" 269 exp_continue 270 } 271 -re "Last login:" { 272 exp_continue 273 } 274 -re "Press the <tab> key \[^\r\n]+\[\r\n]+" { 275 exp_continue 276 } 277 -re "@\[^\r\n]+ $p_prompt" { 278 # ssh pwd prompt 279 sleep 1 280 send -- "$userpswd\r" 281 exp_continue 282 } 283 -re "Enter passphrase.*: " { 284 # sleep briefly to allow time for stty -echo 285 sleep .3 286 send -- "$passphrase\r" 287 exp_continue 288 } 289 -re "$u_prompt" { 290 send -- "$user\r" 291 set uprompt_seen 1 292 exp_continue 293 } 294 -re "$p_prompt" { 295 sleep 1 296 if {$uprompt_seen == 1} { 297 send -- "$userpswd\r" 298 } else { 299 send -- "$passwd\r" 300 } 301 exp_continue 302 } 303 -re "$prompt" { 304 set prompt_match $expect_out(0,string); 305 break; 306 } 307 "Login invalid" { 308 send_user "\nError: Invalid login: $router\n"; 309 catch {close}; catch {wait}; return 1 310 } 311 -re "\[^\r\n]*\[\r\n]+" { exp_continue; } 312 } 313 } 314 315 set in_proc 0 316 return 0 317} 318 319# Enable 320proc do_enable { enauser enapasswd } { 321 global do_saveconfig in_proc 322 global prompt u_prompt e_prompt enacmd 323 set in_proc 1 324 325 send "$enacmd\r" 326 expect { 327 -re "$u_prompt" { send -- "$enauser\r"; exp_continue} 328 -re "$e_prompt" { send -- "$enapasswd\r"; exp_continue} 329 "#" { set prompt "#" } 330 "(enable)" { set prompt "> \\(enable\\) " } 331 "% Invalid input" { 332 send_user "\nError: Unrecognized command, check your enable command\n"; 333 return 1 334 } 335 -re "(denied|Sorry|Incorrect)" { 336 # % Access denied - from local auth and poss. others 337 send_user "\nError: Check your Enable passwd\n"; 338 return 1 339 } 340 "% Error in authentication" { 341 send_user "\nError: Check your Enable passwd\n" 342 return 1 343 } 344 "% Bad passwords" { 345 send_user "\nError: Check your Enable passwd\n" 346 return 1 347 } 348 } 349 # We set the prompt variable (above) so script files don't need 350 # to know what it is. 351 set in_proc 0 352 return 0 353} 354 355# Run commands given on the command line. 356proc run_commands { prompt command } { 357 global do_interact do_saveconfig in_proc platform 358 set in_proc 1 359 360 if { [string compare "extreme" "$platform"] } { 361 # match cisco config mode prompts too, such as router(config-if)#, 362 # but catalyst does not change in this fashion. 363 regsub -lineanchor -- {^(.{1,11}).*([#>])$} $prompt {\1} reprompt 364 regsub -all -- {[\\]$} $reprompt {} reprompt 365 append reprompt {([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} 366 } else { 367 set reprompt $prompt 368 } 369 370 # this is the only way i see to get rid of more prompts in o/p..grrrrr 371 log_user 0 372 373 # handle escaped ;s in commands, and ;; and ^; 374 regsub -all {([^\\]);} $command "\\1\u0002;" esccommand 375 regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command 376 regsub {^;} $command "\u0002;" esccommand 377 regsub -all {[\\];} $esccommand ";" command 378 regsub -all {\u0002;} $command "\u0002" esccommand 379 set sep "\u0002" 380 set commands [split $esccommand $sep] 381 set num_commands [llength $commands] 382 # the pager can not be turned off on the PIX, so we have to look 383 # for the "More" prompt. the extreme is equally obnoxious in pre-12.3 XOS, 384 # with a global switch in the config. 385 for {set i 0} {$i < $num_commands} { incr i} { 386 send -- "[subst -nocommands [lindex $commands $i]]\r" 387 expect { 388 -re "\b+" { exp_continue } 389 -re "^<<Service needs \[^>]+>>" { 390 # for whatever reason, the riverbed prints 391 # this warning in show peers o/p, but doesnt 392 # follow it with a CR, so the following 393 # prompt is not at the BOL, confusing the 394 # parsing script. so, filter it. XXX 395 exp_continue; 396 } 397 -re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)" 398 } 399 -re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)" 400 exp_continue 401 } 402 -re "^--More--\[\r\n]+" { # specific match c1900 pager 403 send " " 404 exp_continue 405 } 406 -re "\[^\r\n]*\[\n\r]+" { send_user -- "$expect_out(buffer)" 407 exp_continue 408 } 409 -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" { 410 send " " 411 # bloody ^[[2K after " " 412 expect { 413 -re "^\[^\r\n]*\r" {} 414 } 415 exp_continue 416 } 417 -re "^ *--More--\[^\n\r]*" { 418 send " " 419 exp_continue } 420 -re "^<-+ More -+>\[^\n\r]*" { 421 send_user -- "$expect_out(buffer)" 422 send " " 423 exp_continue } 424 } 425 } 426 log_user 1 427 428 if { $do_interact == 1 } { 429 interact 430 return 0 431 } 432 433 if { [string compare "extreme" "$platform"] } { 434 send -h "exit\r" 435 } else { 436 send -h "quit\r" 437 } 438 expect { 439 -re "^\[^\n\r *]*$reprompt" { 440 # the Cisco CE and Jnx ERX 441 # return to non-enabled mode 442 # on exit in enabled mode. 443 send -h "exit\r" 444 exp_continue; 445 } 446 "The system has unsaved changes" { # Force10 SFTOS 447 if {$do_saveconfig} { 448 catch {send "y\r"} 449 } else { 450 catch {send "n\r"} 451 } 452 exp_continue 453 } 454 "Would you like to save them now" { # Force10 455 if {$do_saveconfig} { 456 catch {send "y\r"} 457 } else { 458 catch {send "n\r"} 459 } 460 exp_continue 461 } 462 -re "(Profile|Configuration) changes have occurred.*" { 463 # Cisco CSS 464 if {$do_saveconfig} { 465 catch {send "y\r"} 466 } else { 467 catch {send "n\r"} 468 } 469 exp_continue 470 } 471 "Do you wish to save your configuration changes" { 472 if {$do_saveconfig} { 473 catch {send "y\r"} 474 } else { 475 catch {send "n\r"} 476 } 477 exp_continue 478 } 479 -re "\[\n\r]+" { exp_continue } 480 timeout { catch {close}; catch {wait}; 481 return 1 482 } 483 eof { return 0 } 484 } 485 set in_proc 0 486} 487 488# 489# For each router... (this is main loop) 490# 491source_password_file $password_file 492set in_proc 0 493set exitval 0 494set prompt_match "" 495foreach router [lrange $argv $i end] { 496 set router [string tolower $router] 497 # attempt at platform switching. 498 set platform "" 499 send_user -- "$router\n" 500 501 # device timeout 502 set timeout [find timeout $router] 503 if { [llength $timeout] == 0 } { 504 set timeout $timeoutdflt 505 } 506 507 # Default prompt. 508 set prompt [join [find prompt $router] ""] 509 if { [llength $prompt] == 0 } { 510 set prompt "(>|#| \\(enable\\))" 511 } 512 513 # look for autoenable option in .cloginrc & cmd-line 514 set ae [find autoenable $router] 515 if { "$ae" == "1" || $avautoenable } { 516 set autoenable 1 517 } else { 518 set autoenable 0 519 } 520 # look for enable options in .cloginrc & cmd-line 521 if { $avenable == 0 } { 522 set enable 0 523 } else { 524 set ne [find noenable $router] 525 if { "$ne" == "1" || "$autoenable" == "1" } { 526 set enable 0 527 } else { 528 set enable 1 529 } 530 } 531 532 # Figure out passwords 533 if { $do_passwd || $do_enapasswd } { 534 set pswd [find password $router] 535 if { [llength $pswd] == 0 } { 536 send_user -- "\nError: no password for $router in $password_file.\n" 537 continue 538 } 539 if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } { 540 send_user -- "\nError: no enable password for $router in $password_file.\n" 541 continue 542 } 543 if { $do_passwd } { 544 set passwd [join [lindex $pswd 0] ""] 545 } else { 546 set passwd $userpasswd 547 } 548 if { $do_enapasswd } { 549 set enapasswd [join [lindex $pswd 1] ""] 550 } else { 551 set enapasswd $enapasswd 552 } 553 } else { 554 set passwd $userpasswd 555 set enapasswd $enapasswd 556 } 557 558 # Figure out username 559 if {[info exists username]} { 560 # command line username 561 set ruser $username 562 } else { 563 set ruser [join [find user $router] ""] 564 if { "$ruser" == "" } { set ruser $default_user } 565 } 566 567 # Figure out username's password (if different from the vty password) 568 if {[info exists userpasswd]} { 569 # command line username 570 set userpswd $userpasswd 571 } else { 572 set userpswd [join [find userpassword $router] ""] 573 if { "$userpswd" == "" } { set userpswd $passwd } 574 } 575 576 # Figure out enable username 577 if {[info exists enausername]} { 578 # command line enausername 579 set enauser $enausername 580 } else { 581 set enauser [join [find enauser $router] ""] 582 if { "$enauser" == "" } { set enauser $ruser } 583 } 584 585 # Figure out enable command 586 set enacmd [join [find enablecmd $router] ""] 587 if { "$enacmd" == "" } { set enacmd "enable" } 588 589 # Figure out prompts 590 set u_prompt [find userprompt $router] 591 if { "$u_prompt" == "" } { 592 set u_prompt "(\[Uu]sername|Login|login|user name|User):" 593 } else { 594 set u_prompt [join [lindex $u_prompt 0] ""] 595 } 596 set p_prompt [find passprompt $router] 597 if { "$p_prompt" == "" } { 598 set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):" 599 } else { 600 set p_prompt [join [lindex $p_prompt 0] ""] 601 } 602 set e_prompt [find enableprompt $router] 603 if { "$e_prompt" == "" } { 604 set e_prompt "\[Pp]assword:" 605 } else { 606 set e_prompt [join [lindex $e_prompt 0] ""] 607 } 608 609 # Figure out identity file to use 610 set identfile [join [lindex [find identity $router] 0] ""] 611 612 # Figure out passphrase to use 613 if {[info exists avpassphrase]} { 614 set passphrase $avpassphrase 615 } else { 616 set passphrase [join [lindex [find passphrase $router] 0] ""] 617 } 618 if { ! [string length "$passphrase"]} { 619 set passphrase $passwd 620 } 621 622 # Figure out cypher type 623 if {[info exists cypher]} { 624 # command line cypher type 625 set cyphertype $cypher 626 } else { 627 set cyphertype [find cyphertype $router] 628 } 629 630 # Figure out connection method 631 set cmethod [find method $router] 632 if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } 633 634 # Figure out the SSH executable name 635 set sshcmd [join [lindex [find sshcmd $router] 0] ""] 636 if { "$sshcmd" == "" } { set sshcmd {ssh} } 637 638 # Figure out the telnet executable name 639 set telnetcmd [join [lindex [find telnetcmd $router] 0] ""] 640 if { "$telnetcmd" == "" } { set telnetcmd "@TELNET_CMD@" } 641 642 # if [-mM], skip do not login 643 if { $do_cloginrcdbg > 0 } { continue; } 644 645 # Login to the router 646 if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} { 647 incr exitval 648 # if login failed or rsh was unsuccessful, move on to the next device 649 continue 650 } 651 # Figure out the prompt. 652 if { [regexp -- "(#| \\(enable\\))" $prompt_match junk] == 1 } { 653 set enable 0 654 } 655 if { $enable } { 656 if {[do_enable $enauser $enapasswd]} { 657 if { $do_command || $do_script } { 658 incr exitval 659 catch {close}; catch {wait}; 660 continue 661 } 662 } 663 } 664 # we are logged in, now figure out the full prompt 665 send "\r" 666 regsub -all {^(\^*)(.*)} $prompt {\2} reprompt 667 expect { 668 -re "\[\r\n]+" { exp_continue; } 669 -re "^(.+\[:.])1 ($reprompt)" { # stoopid extreme cmd-line numbers and 670 # prompt based on state of config changes, 671 # which may have an * at the beginning. 672 set junk $expect_out(1,string) 673 regsub -all "^\\\* " $expect_out(1,string) {} junk 674 regsub -all "\[\]\[\(\)]" $junk {\\&} junk; 675 set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)"; 676 set platform "extreme" 677 } 678 -re "^.+$reprompt" { set junk $expect_out(0,string); 679 regsub -all "\[\]\[\(\)+]" $junk {\\&} prompt; 680 } 681 } 682 if { $do_command || $do_script } { 683 if { [string compare "extreme" "$platform"] } { 684 # If the prompt is (enable), then we are on a switch and the 685 # command is "set length 0"; otherwise its "terminal length 0". 686 if [regexp -- ".*> .*enable" "$prompt"] { 687 send "set length 0\r" 688 expect -re $prompt {} 689 # XXX This causes the riverbed to reprint the prompt after the 690 # existing prompt, which confuses the expect script. 691 # send "set width 132\r" 692 # expect -re $prompt {} 693 send "set logging session disable\r" 694 } else { 695 send "terminal length 0\r" 696 # XXX This causes the riverbed to reprint the prompt after the 697 # existing prompt, which confuses the expect script. 698 # expect -re $prompt {} 699 # send "terminal width 132\r" 700 } 701 expect -re $prompt {} 702 } else { 703 send "disable clipaging\r" 704 expect -re $prompt {} 705 } 706 } 707 if { $do_command } { 708 if {[run_commands $prompt $command]} { 709 incr exitval 710 continue 711 } 712 } elseif { $do_script } { 713 source $sfile 714 catch {close}; 715 } else { 716 label $router 717 log_user 1 718 interact 719 } 720 721 # End of for each router 722 catch {wait}; 723 sleep 0.3 724} 725exit $exitval 726