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