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