|
Line 0
Link Here
|
|
|
1 |
#! /usr/local/bin/expect -- |
| 2 |
## |
| 3 |
## $Id: cmwlogin 3022 2015-01-13 20:00:00Z heas $ |
| 4 |
## |
| 5 |
## rancid 3.4.1 |
| 6 |
## Copyright (c) 1997-2015 by Terrapin Communications, Inc. |
| 7 |
## All rights reserved. |
| 8 |
## |
| 9 |
## This code is derived from software contributed to and maintained by |
| 10 |
## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan, |
| 11 |
## Pete Whiting, Austin Schutz, and Andrew Fort. |
| 12 |
## |
| 13 |
## Redistribution and use in source and binary forms, with or without |
| 14 |
## modification, are permitted provided that the following conditions |
| 15 |
## are met: |
| 16 |
## 1. Redistributions of source code must retain the above copyright |
| 17 |
## notice, this list of conditions and the following disclaimer. |
| 18 |
## 2. Redistributions in binary form must reproduce the above copyright |
| 19 |
## notice, this list of conditions and the following disclaimer in the |
| 20 |
## documentation and/or other materials provided with the distribution. |
| 21 |
## 3. All advertising materials mentioning features or use of this software |
| 22 |
## must display the following acknowledgement: |
| 23 |
## This product includes software developed by Terrapin Communications, |
| 24 |
## Inc. and its contributors for RANCID. |
| 25 |
## 4. Neither the name of Terrapin Communications, Inc. nor the names of its |
| 26 |
## contributors may be used to endorse or promote products derived from |
| 27 |
## this software without specific prior written permission. |
| 28 |
## 5. It is requested that non-binding fixes and modifications be contributed |
| 29 |
## back to Terrapin Communications, Inc. |
| 30 |
## |
| 31 |
## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS |
| 32 |
## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 33 |
## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 34 |
## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS |
| 35 |
## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 36 |
## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 37 |
## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 38 |
## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 39 |
## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 40 |
## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 41 |
## POSSIBILITY OF SUCH DAMAGE. |
| 42 |
# |
| 43 |
# The expect login scripts were based on Erik Sherk's gwtn, by permission. |
| 44 |
# |
| 45 |
# cwlogin - Comware login |
| 46 |
# |
| 47 |
# Most options are intuitive for logging into a Comware device. |
| 48 |
# The default is to enable (thus -noenable). Some folks have |
| 49 |
# setup tacacs to have a user login at priv-lvl = 3 (enabled) |
| 50 |
# so the -autoenable flag was added for this case (don't go through |
| 51 |
# the process of enabling and the prompt will be the "#" prompt. |
| 52 |
# The default username password is the same as the vty password. |
| 53 |
# |
| 54 |
|
| 55 |
# Usage line |
| 56 |
set usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \ |
| 57 |
\[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \ |
| 58 |
\[-r passphrase\] \[-s script-file\] \[-t timeout\] \[-u username\] \ |
| 59 |
\[-v vty-password\] \[-w enable-username\] \[-x command-file\] \[-P platform\] \ |
| 60 |
\[-y ssh_cypher_type\] router \[router...\]\n" |
| 61 |
|
| 62 |
# env(CLOGIN) may contain: |
| 63 |
# x == do not set xterm banner or name |
| 64 |
|
| 65 |
# Password file |
| 66 |
set password_file $env(HOME)/.cloginrc |
| 67 |
# Default is to login to the router |
| 68 |
set do_command 0 |
| 69 |
set do_script 0 |
| 70 |
# The default is to automatically enable |
| 71 |
set avenable 1 |
| 72 |
# The default is that you login non-enabled (tacacs can have you login already |
| 73 |
# enabled) |
| 74 |
set avautoenable 0 |
| 75 |
# The default is to look in the password file to find the passwords. This |
| 76 |
# tracks if we receive them on the command line. |
| 77 |
set do_passwd 1 |
| 78 |
set do_enapasswd 1 |
| 79 |
# Save config, if prompted |
| 80 |
set do_saveconfig 0 |
| 81 |
# Sometimes routers take awhile to answer (the default is 10 sec) |
| 82 |
set timeoutdflt 45 |
| 83 |
# Some CLIs having problems if we write too fast (Extreme, PIX, Cat) |
| 84 |
set send_human {.2 .1 .4 .2 1} |
| 85 |
|
| 86 |
# Find the user in the ENV, or use the unix userid. |
| 87 |
if {[info exists env(CISCO_USER)]} { |
| 88 |
set default_user $env(CISCO_USER) |
| 89 |
} elseif {[info exists env(USER)]} { |
| 90 |
set default_user $env(USER) |
| 91 |
} elseif {[info exists env(LOGNAME)]} { |
| 92 |
set default_user $env(LOGNAME) |
| 93 |
} else { |
| 94 |
# This uses "id" which I think is portable. At least it has existed |
| 95 |
# (without options) on all machines/OSes I've been on recently - |
| 96 |
# unlike whoami or id -nu. |
| 97 |
if [catch {exec id} reason] { |
| 98 |
send_error "\nError: could not exec id: $reason\n" |
| 99 |
exit 1 |
| 100 |
} |
| 101 |
regexp {\(([^)]*)} "$reason" junk default_user |
| 102 |
} |
| 103 |
if {[info exists env(CLOGINRC)]} { |
| 104 |
set password_file $env(CLOGINRC) |
| 105 |
} |
| 106 |
|
| 107 |
# Process the command line |
| 108 |
for {set i 0} {$i < $argc} {incr i} { |
| 109 |
set arg [lindex $argv $i] |
| 110 |
|
| 111 |
switch -glob -- $arg { |
| 112 |
# Expect debug mode |
| 113 |
-d* { |
| 114 |
exp_internal 1 |
| 115 |
# Username |
| 116 |
} -u* { |
| 117 |
if {! [regexp .\[uU\](.+) $arg ignore user]} { |
| 118 |
incr i |
| 119 |
set username [lindex $argv $i] |
| 120 |
} |
| 121 |
# VTY Password |
| 122 |
} -p* { |
| 123 |
if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} { |
| 124 |
incr i |
| 125 |
set userpasswd [lindex $argv $i] |
| 126 |
} |
| 127 |
set do_passwd 0 |
| 128 |
# ssh passphrase |
| 129 |
} -r* { |
| 130 |
if {! [regexp .\[rR\](.+) $arg ignore passphrase]} { |
| 131 |
incr i |
| 132 |
set vapassphrase [lindex $argv $i] |
| 133 |
} |
| 134 |
# VTY Password |
| 135 |
} -v* { |
| 136 |
if {! [regexp .\[vV\](.+) $arg ignore passwd]} { |
| 137 |
incr i |
| 138 |
set passwd [lindex $argv $i] |
| 139 |
} |
| 140 |
set do_passwd 0 |
| 141 |
# Version string |
| 142 |
} -V* { |
| 143 |
send_user "rancid 3.4.1\n" |
| 144 |
exit 0 |
| 145 |
# Enable Username |
| 146 |
} -w* { |
| 147 |
if {! [regexp .\[wW\](.+) $arg ignore enauser]} { |
| 148 |
incr i |
| 149 |
set enausername [lindex $argv $i] |
| 150 |
} |
| 151 |
# Environment variable to pass to -s scripts |
| 152 |
} -E* { |
| 153 |
if {[regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} { |
| 154 |
set E$varname $varvalue |
| 155 |
} else { |
| 156 |
send_user "\nError: invalid format for -E in $arg\n" |
| 157 |
exit 1 |
| 158 |
} |
| 159 |
# Enable Password |
| 160 |
} -e* { |
| 161 |
if {! [regexp .\[e\](.+) $arg ignore enapasswd]} { |
| 162 |
incr i |
| 163 |
set enapasswd [lindex $argv $i] |
| 164 |
} |
| 165 |
set do_enapasswd 0 |
| 166 |
# Platform |
| 167 |
} -P* { |
| 168 |
if {! [regexp .\[P\](.+) $arg ignore platform]} { |
| 169 |
incr i |
| 170 |
set plat [lindex $argv $P] |
| 171 |
} |
| 172 |
# Command to run. |
| 173 |
} -c* { |
| 174 |
if {! [regexp .\[cC\](.+) $arg ignore command]} { |
| 175 |
incr i |
| 176 |
set command [lindex $argv $i] |
| 177 |
} |
| 178 |
set do_command 1 |
| 179 |
# Expect script to run. |
| 180 |
} -s* { |
| 181 |
if {! [regexp .\[sS\](.+) $arg ignore sfile]} { |
| 182 |
incr i |
| 183 |
set sfile [lindex $argv $i] |
| 184 |
} |
| 185 |
if { ! [file readable $sfile] } { |
| 186 |
send_user "\nError: Can't read $sfile\n" |
| 187 |
exit 1 |
| 188 |
} |
| 189 |
set do_script 1 |
| 190 |
# save config on exit |
| 191 |
} -S* { |
| 192 |
set do_saveconfig 1 |
| 193 |
# 'ssh -c' cypher type |
| 194 |
} -y* { |
| 195 |
if {! [regexp .\[eE\](.+) $arg ignore cypher]} { |
| 196 |
incr i |
| 197 |
set cypher [lindex $argv $i] |
| 198 |
} |
| 199 |
# alternate cloginrc file |
| 200 |
} -f* { |
| 201 |
if {! [regexp .\[fF\](.+) $arg ignore password_file]} { |
| 202 |
incr i |
| 203 |
set password_file [lindex $argv $i] |
| 204 |
} |
| 205 |
# Timeout |
| 206 |
} -t* { |
| 207 |
if {! [regexp .\[tT\](.+) $arg ignore timeout]} { |
| 208 |
incr i |
| 209 |
set timeoutdflt [lindex $argv $i] |
| 210 |
} |
| 211 |
# Command file |
| 212 |
} -x* { |
| 213 |
if {! [regexp .\[xX\](.+) $arg ignore cmd_file]} { |
| 214 |
incr i |
| 215 |
set cmd_file [lindex $argv $i] |
| 216 |
} |
| 217 |
if [catch {set cmd_fd [open $cmd_file r]} reason] { |
| 218 |
send_user "\nError: $reason\n" |
| 219 |
exit 1 |
| 220 |
} |
| 221 |
set cmd_text [read $cmd_fd] |
| 222 |
close $cmd_fd |
| 223 |
set command [join [split $cmd_text \n] \;] |
| 224 |
set do_command 1 |
| 225 |
# Do we enable? |
| 226 |
} -noenable { |
| 227 |
set avenable 0 |
| 228 |
# Does tacacs automatically enable us? |
| 229 |
} -autoenable { |
| 230 |
set avautoenable 1 |
| 231 |
set avenable 0 |
| 232 |
} -* { |
| 233 |
send_user "\nError: Unknown argument! $arg\n" |
| 234 |
send_user $usage |
| 235 |
exit 1 |
| 236 |
} default { |
| 237 |
break |
| 238 |
} |
| 239 |
} |
| 240 |
} |
| 241 |
# Process routers...no routers listed is an error. |
| 242 |
if { $i == $argc } { |
| 243 |
send_user "\nError: $usage" |
| 244 |
} |
| 245 |
|
| 246 |
# Only be quiet if we are running a script (it can log its output |
| 247 |
# on its own) |
| 248 |
if { $do_script } { |
| 249 |
log_user 0 |
| 250 |
} else { |
| 251 |
log_user 1 |
| 252 |
} |
| 253 |
|
| 254 |
# |
| 255 |
# Done configuration/variable setting. Now run with it... |
| 256 |
# |
| 257 |
|
| 258 |
# Sets Xterm title if interactive...if its an xterm and the user cares |
| 259 |
proc label { host } { |
| 260 |
global env |
| 261 |
# if CLOGIN has an 'x' in it, don't set the xterm name/banner |
| 262 |
if [info exists env(CLOGIN)] { |
| 263 |
if {[string first "x" $env(CLOGIN)] != -1} { return } |
| 264 |
} |
| 265 |
# take host from ENV(TERM) |
| 266 |
if [info exists env(TERM)] { |
| 267 |
if [regexp \^(xterm|vs) $env(TERM) ignore] { |
| 268 |
send_user "\033]1;[lindex [split $host "."] 0]\a" |
| 269 |
send_user "\033]2;$host\a" |
| 270 |
} |
| 271 |
} |
| 272 |
} |
| 273 |
|
| 274 |
# This is a helper function to make the password file easier to |
| 275 |
# maintain. Using this the password file has the form: |
| 276 |
# add password sl* pete cow |
| 277 |
# add password at* steve |
| 278 |
# add password * hanky-pie |
| 279 |
proc add {var args} { global int_$var ; lappend int_$var $args} |
| 280 |
proc include {args} { |
| 281 |
global env |
| 282 |
regsub -all "(^{|}$)" $args {} args |
| 283 |
if { [regexp "^/" $args ignore] == 0 } { |
| 284 |
set args $env(HOME)/$args |
| 285 |
} |
| 286 |
source_password_file $args |
| 287 |
} |
| 288 |
|
| 289 |
proc find {var router} { |
| 290 |
upvar int_$var list |
| 291 |
if { [info exists list] } { |
| 292 |
foreach line $list { |
| 293 |
if { [string match -nocase [lindex $line 0] $router] } { |
| 294 |
return [lrange $line 1 end] |
| 295 |
} |
| 296 |
} |
| 297 |
} |
| 298 |
return {} |
| 299 |
} |
| 300 |
|
| 301 |
# Loads the password file. Note that as this file is tcl, and that |
| 302 |
# it is sourced, the user better know what to put in there, as it |
| 303 |
# could install more than just password info... I will assume however, |
| 304 |
# that a "bad guy" could just as easy put such code in the clogin |
| 305 |
# script, so I will leave .cloginrc as just an extention of that script |
| 306 |
proc source_password_file { password_file } { |
| 307 |
global env |
| 308 |
if { ! [file exists $password_file] } { |
| 309 |
send_user "\nError: password file ($password_file) does not exist\n" |
| 310 |
exit 1 |
| 311 |
} |
| 312 |
file stat $password_file fileinfo |
| 313 |
if { [expr ($fileinfo(mode) & 007)] != 0000 } { |
| 314 |
send_user "\nError: $password_file must not be world readable/writable\n" |
| 315 |
exit 1 |
| 316 |
} |
| 317 |
if [catch {source $password_file} reason] { |
| 318 |
send_user "\nError: $reason\n" |
| 319 |
exit 1 |
| 320 |
} |
| 321 |
} |
| 322 |
|
| 323 |
# Log into the router. |
| 324 |
# returns: 0 on success, 1 on failure, -1 if rsh was used successfully |
| 325 |
proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } { |
| 326 |
global command spawn_id in_proc do_command do_script platform passphrase |
| 327 |
global prompt prompt_match u_prompt p_prompt e_prompt sshcmd |
| 328 |
set in_proc 1 |
| 329 |
set uprompt_seen 0 |
| 330 |
|
| 331 |
# try each of the connection methods in $cmethod until one is successful |
| 332 |
set progs [llength $cmethod] |
| 333 |
foreach prog [lrange $cmethod 0 end] { |
| 334 |
incr progs -1 |
| 335 |
if [string match "telnet*" $prog] { |
| 336 |
regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port |
| 337 |
if {"$port" == ""} { |
| 338 |
set retval [catch {spawn telnet $router} reason] |
| 339 |
} else { |
| 340 |
set retval [catch {spawn telnet $router $port} reason] |
| 341 |
} |
| 342 |
if { $retval } { |
| 343 |
send_user "\nError: telnet failed: $reason\n" |
| 344 |
return 1 |
| 345 |
} |
| 346 |
} elseif [string match "ssh*" $prog] { |
| 347 |
# ssh to the router & try to login with or without an identfile. |
| 348 |
regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port |
| 349 |
set cmd $sshcmd |
| 350 |
if {"$port" != ""} { |
| 351 |
set cmd "$cmd -p $port" |
| 352 |
} |
| 353 |
if {"$identfile" != ""} { |
| 354 |
set cmd "$cmd -i $identfile" |
| 355 |
} |
| 356 |
set retval [catch {eval spawn [split "$cmd -c $cyphertype -x -l $user $router" { }]} reason] |
| 357 |
if { $retval } { |
| 358 |
send_user "\nError: $cmd failed: $reason\n" |
| 359 |
return 1 |
| 360 |
} |
| 361 |
} elseif ![string compare $prog "rsh"] { |
| 362 |
if { ! $do_command } { |
| 363 |
if { [llength $cmethod] == 1 } { |
| 364 |
send_user "\nError: rsh is an invalid method for -x and " |
| 365 |
send_user "interactive logins\n" |
| 366 |
} |
| 367 |
if { $progs == 0 } { |
| 368 |
return 1 |
| 369 |
} |
| 370 |
continue; |
| 371 |
} |
| 372 |
|
| 373 |
# handle escaped ;s in commands, and ;; and ^; |
| 374 |
regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand |
| 375 |
regsub {^;} $esccommand "\u002;" command |
| 376 |
set sep "\\1\u001" |
| 377 |
regsub -all {([^\\])\;} $command "$sep" esccommand |
| 378 |
set sep "\u001" |
| 379 |
set commands [split $esccommand $sep] |
| 380 |
set num_commands [llength $commands] |
| 381 |
set rshfail 0 |
| 382 |
for {set i 0} {$i < $num_commands && !$rshfail} { incr i} { |
| 383 |
log_user 0 |
| 384 |
set retval [catch {spawn rsh $user@$router [lindex $commands $i] } reason] |
| 385 |
if { $retval } { |
| 386 |
send_user "\nError: rsh failed: $reason\n" |
| 387 |
log_user 1; return 1 |
| 388 |
} |
| 389 |
send_user "$router# [lindex $commands $i]\n" |
| 390 |
|
| 391 |
# rcmd does not get a pager and no prompts, so we just have to |
| 392 |
# look for failures & lines. |
| 393 |
expect { |
| 394 |
"Connection refused" { catch {close}; catch {wait}; |
| 395 |
send_user "\nError: Connection\ |
| 396 |
Refused ($prog): $router\n" |
| 397 |
set rshfail 1 |
| 398 |
} |
| 399 |
-re "(Connection closed by|Connection to \[^\n\r]+ closed)" { |
| 400 |
catch {close}; catch {wait}; |
| 401 |
send_user "\nError: Connection\ |
| 402 |
closed ($prog): $router\n" |
| 403 |
set rshfail 1 |
| 404 |
} |
| 405 |
"Host is unreachable" { catch {close}; catch {wait}; |
| 406 |
send_user "\nError: Host Unreachable:\ |
| 407 |
$router\n" |
| 408 |
set rshfail 1 |
| 409 |
} |
| 410 |
"No address associated with" { |
| 411 |
catch {close}; catch {wait}; |
| 412 |
send_user "\nError: Unknown host\ |
| 413 |
$router\n" |
| 414 |
set rshfail 1 |
| 415 |
} |
| 416 |
-re "\b+" { exp_continue } |
| 417 |
-re "\[\n\r]+" { send_user -- "$expect_out(buffer)" |
| 418 |
exp_continue |
| 419 |
} |
| 420 |
timeout { catch {close}; catch {wait}; |
| 421 |
send_user "\nError: TIMEOUT reached\n" |
| 422 |
set rshfail 1 |
| 423 |
} |
| 424 |
eof { catch {close}; catch {wait}; } |
| 425 |
} |
| 426 |
log_user 1 |
| 427 |
} |
| 428 |
if { $rshfail } { |
| 429 |
if { !$progs } { |
| 430 |
return 1 |
| 431 |
} else { |
| 432 |
continue |
| 433 |
} |
| 434 |
} |
| 435 |
# fake the end of the session for rancid. |
| 436 |
send_user "$router# exit\n" |
| 437 |
# return rsh "success" |
| 438 |
return -1 |
| 439 |
} else { |
| 440 |
send_user "\nError: unknown connection method: $prog\n" |
| 441 |
return 1 |
| 442 |
} |
| 443 |
sleep 0.3 |
| 444 |
|
| 445 |
# This helps cleanup each expect clause. |
| 446 |
expect_after { |
| 447 |
timeout { |
| 448 |
send_user "\nError: TIMEOUT reached\n" |
| 449 |
catch {close}; catch {wait}; |
| 450 |
if { $in_proc} { |
| 451 |
return 1 |
| 452 |
} else { |
| 453 |
continue |
| 454 |
} |
| 455 |
} eof { |
| 456 |
send_user "\nError: EOF received\n" |
| 457 |
catch {close}; catch {wait}; |
| 458 |
if { $in_proc} { |
| 459 |
return 1 |
| 460 |
} else { |
| 461 |
continue |
| 462 |
} |
| 463 |
} |
| 464 |
} |
| 465 |
|
| 466 |
# Here we get a little tricky. There are several possibilities: |
| 467 |
# the router can ask for a username and passwd and then |
| 468 |
# talk to the TACACS server to authenticate you, or if the |
| 469 |
# TACACS server is not working, then it will use the enable |
| 470 |
# passwd. Or, the router might not have TACACS turned on, |
| 471 |
# then it will just send the passwd. |
| 472 |
# if telnet fails with connection refused, try ssh |
| 473 |
expect { |
| 474 |
-re "^<-+ More -+>\[^\n\r]*" { |
| 475 |
# ASA will use the pager for long banners |
| 476 |
send " "; |
| 477 |
exp_continue |
| 478 |
} |
| 479 |
-re "(Connection refused|Secure connection \[^\n\r]+ refused)" { |
| 480 |
catch {close}; catch {wait}; |
| 481 |
if !$progs { |
| 482 |
send_user "\nError: Connection Refused ($prog): $router\n" |
| 483 |
return 1 |
| 484 |
} |
| 485 |
} |
| 486 |
-re "(Connection closed by|Connection to \[^\n\r]+ closed)" { |
| 487 |
catch {close}; catch {wait}; |
| 488 |
if !$progs { |
| 489 |
send_user "\nError: Connection closed ($prog): $router\n" |
| 490 |
return 1 |
| 491 |
} |
| 492 |
} |
| 493 |
eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 } |
| 494 |
-nocase "unknown host\r" { |
| 495 |
send_user "\nError: Unknown host $router\n"; |
| 496 |
catch {close}; catch {wait}; |
| 497 |
return 1 |
| 498 |
} |
| 499 |
"Host is unreachable" { |
| 500 |
send_user "\nError: Host Unreachable: $router\n"; |
| 501 |
catch {close}; catch {wait}; |
| 502 |
return 1 |
| 503 |
} |
| 504 |
"No address associated with name" { |
| 505 |
send_user "\nError: Unknown host $router\n"; |
| 506 |
catch {close}; catch {wait}; |
| 507 |
return 1 |
| 508 |
} |
| 509 |
-re "(Host key not found |The authenticity of host .* be established).* \\(yes/no\\)\\?" { |
| 510 |
send "yes\r" |
| 511 |
send_user "\nHost $router added to the list of known hosts.\n" |
| 512 |
exp_continue |
| 513 |
} |
| 514 |
-re "HOST IDENTIFICATION HAS CHANGED.* \\(yes/no\\)\\?" { |
| 515 |
send "no\r" |
| 516 |
send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" |
| 517 |
catch {close}; catch {wait}; |
| 518 |
return 1 |
| 519 |
} |
| 520 |
-re "HOST IDENTIFICATION HAS CHANGED\[^\n\r]+" { |
| 521 |
send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" |
| 522 |
return 1 |
| 523 |
} |
| 524 |
-re "Offending key for .* \\(yes/no\\)\\?" { |
| 525 |
send "no\r" |
| 526 |
send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" |
| 527 |
catch {close}; catch {wait}; |
| 528 |
return 1 |
| 529 |
} |
| 530 |
-re "(denied|Sorry)" { |
| 531 |
send_user "\nError: Check your passwd for $router\n" |
| 532 |
catch {close}; catch {wait}; return 1 |
| 533 |
} |
| 534 |
"Login failed" { |
| 535 |
send_user "\nError: Check your passwd for $router\n" |
| 536 |
catch {close}; catch {wait}; return 1 |
| 537 |
} |
| 538 |
-re "% (Bad passwords|Authentication failed)" { |
| 539 |
send_user "\nError: Check your passwd for $router\n" |
| 540 |
catch {close}; catch {wait}; return 1 |
| 541 |
} |
| 542 |
"Press any key to continue" { |
| 543 |
# send_user "Pressing the ANY key\n" |
| 544 |
send "\r" |
| 545 |
exp_continue |
| 546 |
} |
| 547 |
-re "Enter Selection: " { |
| 548 |
# Catalyst 1900s have some lame menu. Enter |
| 549 |
# K to reach a command-line. |
| 550 |
send "K\r" |
| 551 |
exp_continue |
| 552 |
} |
| 553 |
-re "Last login:" { |
| 554 |
exp_continue |
| 555 |
} |
| 556 |
-re "Press the <tab> key \[^\r\n]+\[\r\n]+" { |
| 557 |
exp_continue |
| 558 |
} |
| 559 |
-re "@\[^\r\n]+ $p_prompt" { |
| 560 |
# ssh pwd prompt |
| 561 |
sleep 1 |
| 562 |
send -- "$userpswd\r" |
| 563 |
exp_continue |
| 564 |
} |
| 565 |
-re "Enter passphrase.*: " { |
| 566 |
# sleep briefly to allow time for stty -echo |
| 567 |
sleep .3 |
| 568 |
send -- "$passphrase\r" |
| 569 |
exp_continue |
| 570 |
} |
| 571 |
-re "$u_prompt" { |
| 572 |
send -- "$user\r" |
| 573 |
set uprompt_seen 1 |
| 574 |
exp_continue |
| 575 |
} |
| 576 |
-re "$p_prompt" { |
| 577 |
sleep 1 |
| 578 |
if {$uprompt_seen == 1} { |
| 579 |
send -- "$userpswd\r" |
| 580 |
} else { |
| 581 |
send -- "$passwd\r" |
| 582 |
} |
| 583 |
exp_continue |
| 584 |
} |
| 585 |
-re "$prompt" { |
| 586 |
set prompt_match $expect_out(0,string); |
| 587 |
break; |
| 588 |
} |
| 589 |
"Login invalid" { |
| 590 |
send_user "\nError: Invalid login: $router\n"; |
| 591 |
catch {close}; catch {wait}; return 1 |
| 592 |
} |
| 593 |
-re "\[^\r\n]*\[\r\n]+" { exp_continue; } |
| 594 |
} |
| 595 |
} |
| 596 |
|
| 597 |
set in_proc 0 |
| 598 |
return 0 |
| 599 |
} |
| 600 |
|
| 601 |
# Enable |
| 602 |
proc do_enable { enauser enapasswd } { |
| 603 |
global do_saveconfig in_proc |
| 604 |
global prompt u_prompt e_prompt enacmd |
| 605 |
set in_proc 1 |
| 606 |
|
| 607 |
send "$enacmd\r" |
| 608 |
expect { |
| 609 |
-re "$u_prompt" { send -- "$enauser\r"; exp_continue} |
| 610 |
-re "$e_prompt" { send -- "$enapasswd\r"; exp_continue} |
| 611 |
">" { set prompt ">" } |
| 612 |
"#" { set prompt "#" } |
| 613 |
"(enable)" { set prompt "> \\(enable\\) " } |
| 614 |
"% Invalid input" { |
| 615 |
send_user "\nError: Unrecognized command, check your enable command\n"; |
| 616 |
return 1 |
| 617 |
} |
| 618 |
-re "(denied|Sorry|Incorrect)" { |
| 619 |
# % Access denied - from local auth and poss. others |
| 620 |
send_user "\nError: Check your Enable passwd\n"; |
| 621 |
return 1 |
| 622 |
} |
| 623 |
"% Error in authentication" { |
| 624 |
send_user "\nError: Check your Enable passwd\n" |
| 625 |
return 1 |
| 626 |
} |
| 627 |
"% Bad passwords" { |
| 628 |
send_user "\nError: Check your Enable passwd\n" |
| 629 |
return 1 |
| 630 |
} |
| 631 |
"% Password is not set." { # Comware |
| 632 |
send_user "\nError: No '$enacmd' password set for devide\n" |
| 633 |
return 1 |
| 634 |
} |
| 635 |
"% Authenticate failed." { # Comware |
| 636 |
send_user "\nError: Check your enable password for '$enacmd'\n |
| 637 |
return 1 |
| 638 |
} |
| 639 |
} |
| 640 |
# We set the prompt variable (above) so script files don't need |
| 641 |
# to know what it is. |
| 642 |
set in_proc 0 |
| 643 |
return 0 |
| 644 |
} |
| 645 |
|
| 646 |
# Run commands given on the command line. |
| 647 |
proc run_commands { prompt command } { |
| 648 |
global do_saveconfig in_proc platform exitcmd |
| 649 |
set in_proc 1 |
| 650 |
|
| 651 |
# If the prompt is (enable), then we are on a switch and the |
| 652 |
# command is "set length 0"; otherwise its "terminal length 0". |
| 653 |
# skip if its an extreme (since the pager can not be disabled on a |
| 654 |
# per-vty basis). |
| 655 |
if { [string compare "extreme" "$platform"] } { |
| 656 |
# match cisco config mode prompts too, such as router(config-if)#, |
| 657 |
# but catalyst does not change in this fashion. |
| 658 |
regsub -all {^(.{1,11}).*([#>])$} $prompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt |
| 659 |
} else { |
| 660 |
set reprompt $prompt |
| 661 |
} |
| 662 |
|
| 663 |
# this is the only way i see to get rid of more prompts in o/p..grrrrr |
| 664 |
log_user 0 |
| 665 |
|
| 666 |
# handle escaped ;s in commands, and ;; and ^; |
| 667 |
regsub -all {([^\\]);;} $command "\\1;\u002;" esccommand |
| 668 |
regsub {^;} $esccommand "\u002;" command |
| 669 |
set sep "\\1\u001" |
| 670 |
regsub -all {([^\\]);} $command "$sep" esccommand |
| 671 |
set sep "\u001" |
| 672 |
set commands [split $esccommand $sep] |
| 673 |
set num_commands [llength $commands] |
| 674 |
# the pager can not be turned off on the PIX, so we have to look |
| 675 |
# for the "More" prompt. the extreme is equally obnoxious in pre-12.3 XOS, |
| 676 |
# with a global switch in the config. |
| 677 |
for {set i 0} {$i < $num_commands} { incr i} { |
| 678 |
if { [lindex $commands $i] == "\u002" } { |
| 679 |
send -- "\r" |
| 680 |
} else { |
| 681 |
send -- "[subst -nocommands [lindex $commands $i]]\r" |
| 682 |
} |
| 683 |
expect { |
| 684 |
-re "\b+" { exp_continue } |
| 685 |
-re "^\[^\n\r *]*$reprompt" { send_user -- "$expect_out(buffer)" |
| 686 |
} |
| 687 |
-re "^\[^\n\r]*$reprompt." { send_user -- "$expect_out(buffer)" |
| 688 |
exp_continue |
| 689 |
} |
| 690 |
-re "^--More--\[\r\n]+" { # specific match c1900 pager |
| 691 |
send " " |
| 692 |
exp_continue |
| 693 |
} |
| 694 |
-re "\[^\r\n]*\[\n\r]+" { send_user -- "$expect_out(buffer)" |
| 695 |
exp_continue |
| 696 |
} |
| 697 |
-re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" { |
| 698 |
send " " |
| 699 |
# bloody ^[[2K after " " |
| 700 |
expect { |
| 701 |
-re "^\[^\r\n]*\r" {} |
| 702 |
} |
| 703 |
exp_continue |
| 704 |
} |
| 705 |
-re "^ *--More--\[^\n\r]*" { |
| 706 |
send " " |
| 707 |
exp_continue } |
| 708 |
-re "^<-+ More -+>\[^\n\r]*" { |
| 709 |
send_user -- "$expect_out(buffer)" |
| 710 |
send " " |
| 711 |
exp_continue } |
| 712 |
-re "^ {0,2}-+ More .*-+.*\[^\n\r]*" { |
| 713 |
# Comware 3/5 pager prompt |
| 714 |
sleep 0.1 |
| 715 |
send " " |
| 716 |
exp_continue } |
| 717 |
-re "^---- More ----\[^\n\r]*" { |
| 718 |
# Comware 7 pager prompt |
| 719 |
sleep 0.1 |
| 720 |
send " " |
| 721 |
exp_continue } |
| 722 |
} |
| 723 |
} |
| 724 |
log_user 1 |
| 725 |
|
| 726 |
if { [string compare "extreme" "$platform"] } { |
| 727 |
send -h "exit\r" |
| 728 |
} else { |
| 729 |
send -h "quit\r" |
| 730 |
} |
| 731 |
expect { |
| 732 |
-re "^\[^\n\r *]*$reprompt" { |
| 733 |
# the Cisco CE and Jnx ERX |
| 734 |
# return to non-enabled mode |
| 735 |
# on exit in enabled mode. |
| 736 |
send -h "$exitcmd\r" |
| 737 |
exp_continue; |
| 738 |
} |
| 739 |
"The system has unsaved changes" { # Force10 SFTOS |
| 740 |
if {$do_saveconfig} { |
| 741 |
catch {send "y\r"} |
| 742 |
} else { |
| 743 |
catch {send "n\r"} |
| 744 |
} |
| 745 |
exp_continue |
| 746 |
} |
| 747 |
"Would you like to save them now" { # Force10 |
| 748 |
if {$do_saveconfig} { |
| 749 |
catch {send "y\r"} |
| 750 |
} else { |
| 751 |
catch {send "n\r"} |
| 752 |
} |
| 753 |
exp_continue |
| 754 |
} |
| 755 |
-re "(Profile|Configuration) changes have occurred.*" { |
| 756 |
# Cisco CSS |
| 757 |
if {$do_saveconfig} { |
| 758 |
catch {send "y\r"} |
| 759 |
} else { |
| 760 |
catch {send "n\r"} |
| 761 |
} |
| 762 |
exp_continue |
| 763 |
} |
| 764 |
"Do you wish to save your configuration changes" { |
| 765 |
if {$do_saveconfig} { |
| 766 |
catch {send "y\r"} |
| 767 |
} else { |
| 768 |
catch {send "n\r"} |
| 769 |
} |
| 770 |
exp_continue |
| 771 |
} |
| 772 |
-re "\[\n\r]+" { exp_continue } |
| 773 |
-re "\[^\n\r *]Note:" { return 0 } |
| 774 |
timeout { catch {close}; catch {wait}; |
| 775 |
return 0 |
| 776 |
} |
| 777 |
eof { return 0 } |
| 778 |
} |
| 779 |
set in_proc 0 |
| 780 |
} |
| 781 |
|
| 782 |
# |
| 783 |
# For each router... (this is main loop) |
| 784 |
# |
| 785 |
source_password_file $password_file |
| 786 |
set in_proc 0 |
| 787 |
set exitval 0 |
| 788 |
set prompt_match "" |
| 789 |
foreach router [lrange $argv $i end] { |
| 790 |
set router [string tolower $router] |
| 791 |
# attempt at platform switching. |
| 792 |
set platform "" |
| 793 |
send_user -- "$router\n" |
| 794 |
|
| 795 |
# Figure out the platform |
| 796 |
if {[info exists plat]} { |
| 797 |
# command line platform |
| 798 |
set platform $plat |
| 799 |
} else { |
| 800 |
set plat [find platform $router] |
| 801 |
if { "$plat" == "" } { set platform "" } |
| 802 |
} |
| 803 |
|
| 804 |
# Based on the platform, set some other defaults |
| 805 |
# These can get overridden by other explicit settings |
| 806 |
# Note that MA5600 wants enacmd "enable" |
| 807 |
# Note that later versions of Coomware want cyphertype "aes128-cbc" |
| 808 |
if { [string compare "cmw" "$platform"] } { |
| 809 |
set enacmd "super" |
| 810 |
set exitcmd "quit" |
| 811 |
set cyphertype "3des" |
| 812 |
} else { |
| 813 |
set exitcmd "exit" |
| 814 |
} |
| 815 |
|
| 816 |
# device timeout |
| 817 |
set timeout [find timeout $router] |
| 818 |
if { [llength $timeout] == 0 } { |
| 819 |
set timeout $timeoutdflt |
| 820 |
} |
| 821 |
|
| 822 |
# Default prompt. |
| 823 |
set prompt [join [find prompt $router] ""] |
| 824 |
if { [llength $prompt] == 0 } { |
| 825 |
set prompt "(>\a?|#| \\(enable\\))" |
| 826 |
} |
| 827 |
|
| 828 |
# look for autoenable option in .cloginrc & cmd-line |
| 829 |
set ae [find autoenable $router] |
| 830 |
if { "$ae" == "1" || $avautoenable } { |
| 831 |
set autoenable 1 |
| 832 |
} else { |
| 833 |
set autoenable 0 |
| 834 |
} |
| 835 |
# look for enable options in .cloginrc & cmd-line |
| 836 |
if { $avenable == 0 } { |
| 837 |
set enable 0 |
| 838 |
} else { |
| 839 |
set ne [find noenable $router] |
| 840 |
if { "$ne" == "1" || "$autoenable" == "1" } { |
| 841 |
set enable 0 |
| 842 |
} else { |
| 843 |
set enable 1 |
| 844 |
} |
| 845 |
} |
| 846 |
|
| 847 |
# Figure out passwords |
| 848 |
if { $do_passwd || $do_enapasswd } { |
| 849 |
set pswd [find password $router] |
| 850 |
if { [llength $pswd] == 0 } { |
| 851 |
send_user -- "\nError: no password for $router in $password_file.\n" |
| 852 |
continue |
| 853 |
} |
| 854 |
if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } { |
| 855 |
send_user -- "\nError: no enable password for $router in $password_file.\n" |
| 856 |
continue |
| 857 |
} |
| 858 |
set passwd [join [lindex $pswd 0] ""] |
| 859 |
set enapasswd [join [lindex $pswd 1] ""] |
| 860 |
} else { |
| 861 |
set passwd $userpasswd |
| 862 |
set enapasswd $enapasswd |
| 863 |
} |
| 864 |
|
| 865 |
# Figure out username |
| 866 |
if {[info exists username]} { |
| 867 |
# command line username |
| 868 |
set ruser $username |
| 869 |
} else { |
| 870 |
set ruser [join [find user $router] ""] |
| 871 |
if { "$ruser" == "" } { set ruser $default_user } |
| 872 |
} |
| 873 |
|
| 874 |
# Figure out username's password (if different from the vty password) |
| 875 |
if {[info exists userpasswd]} { |
| 876 |
# command line username |
| 877 |
set userpswd $userpasswd |
| 878 |
} else { |
| 879 |
set userpswd [join [find userpassword $router] ""] |
| 880 |
if { "$userpswd" == "" } { set userpswd $passwd } |
| 881 |
} |
| 882 |
|
| 883 |
# Figure out enable username |
| 884 |
if {[info exists enausername]} { |
| 885 |
# command line enausername |
| 886 |
set enauser $enausername |
| 887 |
} else { |
| 888 |
set enauser [join [find enauser $router] ""] |
| 889 |
if { "$enauser" == "" } { set enauser $ruser } |
| 890 |
} |
| 891 |
|
| 892 |
# Figure out enable command |
| 893 |
set enacmd [join [find enablecmd $router] ""] |
| 894 |
if { "$enacmd" == "" } { set enacmd "enable" } |
| 895 |
|
| 896 |
# Figure out prompts |
| 897 |
set u_prompt [find userprompt $router] |
| 898 |
if { "$u_prompt" == "" } { |
| 899 |
set u_prompt "(\[Uu]sername|Login|login|user name|User|User name):" |
| 900 |
} else { |
| 901 |
set u_prompt [join [lindex $u_prompt 0] ""] |
| 902 |
} |
| 903 |
set p_prompt [find passprompt $router] |
| 904 |
if { "$p_prompt" == "" } { |
| 905 |
set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):" |
| 906 |
} else { |
| 907 |
set p_prompt [join [lindex $p_prompt 0] ""] |
| 908 |
} |
| 909 |
set e_prompt [find enableprompt $router] |
| 910 |
if { "$e_prompt" == "" } { |
| 911 |
set e_prompt "\[Pp]assword:" |
| 912 |
} else { |
| 913 |
set e_prompt [join [lindex $e_prompt 0] ""] |
| 914 |
} |
| 915 |
|
| 916 |
# Figure out identity file to use |
| 917 |
set identfile [join [lindex [find identity $router] 0] ""] |
| 918 |
|
| 919 |
# Figure out passphrase to use |
| 920 |
if {[info exists avpassphrase]} { |
| 921 |
set passphrase $avpassphrase |
| 922 |
} else { |
| 923 |
set passphrase [join [lindex [find passphrase $router] 0] ""] |
| 924 |
} |
| 925 |
if { ! [string length "$passphrase"]} { |
| 926 |
set passphrase $passwd |
| 927 |
} |
| 928 |
|
| 929 |
# Figure out cypher type |
| 930 |
if {[info exists cypher]} { |
| 931 |
# command line cypher type |
| 932 |
set cyphertype $cypher |
| 933 |
} else { |
| 934 |
set cyphertype [find cyphertype $router] |
| 935 |
if { "$cyphertype" == "" } { set cyphertype "3des" } |
| 936 |
} |
| 937 |
|
| 938 |
# Figure out connection method |
| 939 |
set cmethod [find method $router] |
| 940 |
if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } |
| 941 |
|
| 942 |
# Figure out the SSH executable name |
| 943 |
set sshcmd [join [lindex [find sshcmd $router] 0] ""] |
| 944 |
if { "$sshcmd" == "" } { set sshcmd {ssh} } |
| 945 |
|
| 946 |
|
| 947 |
# Login to the router |
| 948 |
if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} { |
| 949 |
incr exitval |
| 950 |
# if login failed or rsh was unsuccessful, move on to the next device |
| 951 |
continue |
| 952 |
} |
| 953 |
# Figure out the prompt. |
| 954 |
if { [regexp -- "(#| \\(enable\\))" $prompt_match junk] == 1 } { |
| 955 |
set enable 0 |
| 956 |
} |
| 957 |
|
| 958 |
# Disable smart and interactive before send other commands |
| 959 |
# (MA5600 and similar only?) |
| 960 |
# Also disable log junk being sent to terminal |
| 961 |
if { [string compare "cmw" "$platform"] } { |
| 962 |
send -h "undo smart\r" |
| 963 |
expect -re $prompt {} |
| 964 |
send -h "undo interactive\r" |
| 965 |
expect -re $prompt {} |
| 966 |
send -h "undo terminal monitor\r" |
| 967 |
expect -re $prompt {} |
| 968 |
} |
| 969 |
|
| 970 |
if { $enable } { |
| 971 |
if {[do_enable $enauser $enapasswd]} { |
| 972 |
if { $do_command || $do_script } { |
| 973 |
incr exitval |
| 974 |
catch {close}; catch {wait}; |
| 975 |
continue |
| 976 |
} |
| 977 |
} |
| 978 |
} |
| 979 |
# we are logged in, now figure out the full prompt |
| 980 |
send "\r" |
| 981 |
regsub -all {^(\^*)(.*)} $prompt {\2} reprompt |
| 982 |
expect { |
| 983 |
-re "\[\r\n]+" { exp_continue; } |
| 984 |
-re "^(.+\[:.])1 ($reprompt)" { # stoopid extreme cmd-line numbers and |
| 985 |
# prompt based on state of config changes, |
| 986 |
# which may have an * at the beginning. |
| 987 |
set junk $expect_out(1,string) |
| 988 |
regsub -all "^\\\* " $expect_out(1,string) {} junk |
| 989 |
regsub -all "\[\]\[\(\)]" $junk {\\&} junk; |
| 990 |
set prompt ".? ?$junk\[0-9]+ $expect_out(2,string)"; |
| 991 |
set platform "extreme" |
| 992 |
} |
| 993 |
-re "^.+$reprompt" { set junk $expect_out(0,string); |
| 994 |
regsub -all "\[\]\[\(\)+]" $junk {\\&} prompt; |
| 995 |
} |
| 996 |
} |
| 997 |
if { $do_command || $do_script } { |
| 998 |
if { [string compare "extreme" "$platform"] } { |
| 999 |
# If the prompt is (enable), then we are on a switch and the |
| 1000 |
# command is "set length 0"; otherwise its "terminal length 0". |
| 1001 |
if [regexp -- ".*> .*enable" "$prompt"] { |
| 1002 |
send "set length 0\r" |
| 1003 |
expect -re $prompt {} |
| 1004 |
send "set width 132\r" |
| 1005 |
expect -re $prompt {} |
| 1006 |
send "set logging session disable\r" |
| 1007 |
} else { |
| 1008 |
send "terminal length 0\r" |
| 1009 |
expect -re $prompt {} |
| 1010 |
send "terminal width 132\r" |
| 1011 |
} |
| 1012 |
expect -re $prompt {} |
| 1013 |
} elseif { [string compare "cmw" "$platform"] } { |
| 1014 |
# Turn session paging off |
| 1015 |
# Comware 5/7 only. |
| 1016 |
# Comware 3 models have a screen-length command that only works on |
| 1017 |
# a vty basis |
| 1018 |
send -h "screen-length disable\r" |
| 1019 |
expect -re $prompt {} |
| 1020 |
} else { |
| 1021 |
send "disable clipaging\r" |
| 1022 |
expect -re $prompt {} |
| 1023 |
} |
| 1024 |
} |
| 1025 |
if { $do_command } { |
| 1026 |
if {[run_commands $prompt $command]} { |
| 1027 |
incr exitval |
| 1028 |
continue |
| 1029 |
} |
| 1030 |
} elseif { $do_script } { |
| 1031 |
source $sfile |
| 1032 |
catch {close}; |
| 1033 |
} else { |
| 1034 |
label $router |
| 1035 |
log_user 1 |
| 1036 |
interact |
| 1037 |
} |
| 1038 |
|
| 1039 |
# End of for each router |
| 1040 |
catch {wait}; |
| 1041 |
sleep 0.3 |
| 1042 |
} |
| 1043 |
exit $exitval |