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