Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #! /usr/bin/expect --
- ##
- ## $Id: blogin.in,v 1.50 2009/04/16 21:22:57 heas Exp $
- ##
- ## @PACKAGE@ @VERSION@
- ## Copyright (c) 1997-2009 by Terrapin Communications, Inc.
- ## All rights reserved.
- ##
- ## This code is derived from software contributed to and maintained by
- ## Terrapin Communications, Inc. by Henry Kilmer, John Heasley, Andrew Partan,
- ## Pete Whiting, Austin Schutz, and Andrew Fort.
- ##
- ## Redistribution and use in source and binary forms, with or without
- ## modification, are permitted provided that the following conditions
- ## are met:
- ## 1. Redistributions of source code must retain the above copyright
- ## notice, this list of conditions and the following disclaimer.
- ## 2. Redistributions in binary form must reproduce the above copyright
- ## notice, this list of conditions and the following disclaimer in the
- ## documentation and/or other materials provided with the distribution.
- ## 3. All advertising materials mentioning features or use of this software
- ## must display the following acknowledgement:
- ## This product includes software developed by Terrapin Communications,
- ## Inc. and its contributors for RANCID.
- ## 4. Neither the name of Terrapin Communications, Inc. nor the names of its
- ## contributors may be used to endorse or promote products derived from
- ## this software without specific prior written permission.
- ## 5. It is requested that non-binding fixes and modifications be contributed
- ## back to Terrapin Communications, Inc.
- ##
- ## THIS SOFTWARE IS PROVIDED BY Terrapin Communications, INC. AND CONTRIBUTORS
- ## ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- ## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- ## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COMPANY OR CONTRIBUTORS
- ## BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- ## CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- ## SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- ## INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- ## CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- ## POSSIBILITY OF SUCH DAMAGE.
- #
- # The expect login scripts were based on Erik Sherk's gwtn, by permission.
- #
- # blogin - Bay Networks(Nortel) login
- #
- # Unlike the Cisco's, there is no enable function on the Bay's. Instead
- # there are seperate User and Manager accounts. A 'system' command exists,
- # which I am told does nothing.
- #
- # The "bcc>" prompt changes to "box#", not "bcc#" after the config command.
- #
- # Usage line
- set usage "Usage: $argv0 \[-dSV\] \[-autoenable\] \[-noenable\] \[-c command\] \
- \[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \[-p user-password\] \
- \[-s script-file\] \[-t timeout\] \[-u username\] \
- \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \
- \[-y ssh_cypher_type\] router \[router...\]\n"
- # env(CLOGIN) may contain:
- # x == do not set xterm banner or name
- # Password file
- set password_file $env(HOME)/.cloginrc
- # Default is to login to the router
- set do_command 0
- set do_script 0
- # The default is to automatically enable
- set avenable 0
- # The default is that you login non-enabled (tacacs can have you login already
- # enabled)
- set avautoenable 0
- # The default is to look in the password file to find the passwords. This
- # tracks if we receive them on the command line.
- set do_passwd 1
- set do_enapasswd 0
- # Save config, if prompted
- set do_saveconfig 0
- # Find the user in the ENV, or use the unix userid.
- if {[ info exists env(CISCO_USER) ]} {
- set default_user $env(CISCO_USER)
- } elseif {[ info exists env(USER) ]} {
- set default_user $env(USER)
- } elseif {[ info exists env(LOGNAME) ]} {
- set default_user $env(LOGNAME)
- } else {
- # This uses "id" which I think is portable. At least it has existed
- # (without options) on all machines/OSes I've been on recently -
- # unlike whoami or id -nu.
- if [ catch {exec id} reason ] {
- send_error "\nError: could not exec id: $reason\n"
- exit 1
- }
- regexp {\(([^)]*)} "$reason" junk default_user
- }
- if {[ info exists env(CLOGINRC) ]} {
- set password_file $env(CLOGINRC)
- }
- # Sometimes routers take awhile to answer (the default is 10 sec)
- set timeout 45
- # Process the command line
- for {set i 0} {$i < $argc} {incr i} {
- set arg [lindex $argv $i]
- switch -glob -- $arg {
- # Expect debug mode
- -d* {
- exp_internal 1
- # Username
- } -u* {
- if {! [ regexp .\[uU\](.+) $arg ignore user]} {
- incr i
- set username [ lindex $argv $i ]
- }
- # VTY Password
- } -p* {
- if {! [ regexp .\[pP\](.+) $arg ignore userpasswd]} {
- incr i
- set userpasswd [ lindex $argv $i ]
- }
- set do_passwd 0
- # VTY Password
- } -v* {
- if {! [ regexp .\[vV\](.+) $arg ignore passwd]} {
- incr i
- set passwd [ lindex $argv $i ]
- }
- set do_passwd 0
- # Version string
- } -V* {
- send_user "@PACKAGE@ @VERSION@\n"
- exit 0
- # Enable Username
- } -w* {
- if {! [ regexp .\[wW\](.+) $arg ignore enauser]} {
- incr i
- set enausername [ lindex $argv $i ]
- }
- # Environment variable to pass to -s scripts
- } -E* {
- if {[ regexp .\[E\](.+)=(.+) $arg ignore varname varvalue]} {
- set E$varname $varvalue
- } else {
- send_user "\nError: invalid format for -E in $arg\n"
- exit 1
- }
- # Enable Password
- } -e* {
- if {! [ regexp .\[eE\](.+) $arg ignore enapasswd]} {
- incr i
- set enapasswd [ lindex $argv $i ]
- }
- set do_enapasswd 0
- # Command to run.
- } -c* {
- if {! [ regexp .\[cC\](.+) $arg ignore command]} {
- incr i
- set command [ lindex $argv $i ]
- }
- set do_command 1
- # Expect script to run.
- } -s* {
- if {! [ regexp .\[sS\](.+) $arg ignore sfile]} {
- incr i
- set sfile [ lindex $argv $i ]
- }
- if { ! [ file readable $sfile ] } {
- send_user "\nError: Can't read $sfile\n"
- exit 1
- }
- set do_script 1
- # save config on exit
- } -S* {
- set do_saveconfig 1
- # 'ssh -c' cypher type
- } -y* {
- if {! [ regexp .\[eE\](.+) $arg ignore cypher]} {
- incr i
- set cypher [ lindex $argv $i ]
- }
- # alternate cloginrc file
- } -f* {
- if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
- incr i
- set password_file [ lindex $argv $i ]
- }
- # Timeout
- } -t* {
- if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
- incr i
- set timeout [ lindex $argv $i ]
- }
- # Command file
- } -x* {
- if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} {
- incr i
- set cmd_file [ lindex $argv $i ]
- }
- set cmd_fd [open $cmd_file r]
- set cmd_text [read $cmd_fd]
- close $cmd_fd
- set command [join [split $cmd_text \n] \;]
- set do_command 1
- # Do we enable?
- } -noenable {
- set avenable 0
- # Does tacacs automatically enable us?
- } -autoenable {
- set avautoenable 1
- set avenable 0
- } -* {
- send_user "\nError: Unknown argument! $arg\n"
- send_user $usage
- exit 1
- } default {
- break
- }
- }
- }
- # Process routers...no routers listed is an error.
- if { $i == $argc } {
- send_user "\nError: $usage"
- }
- # Only be quiet if we are running a script (it can log its output
- # on its own)
- if { $do_script } {
- log_user 0
- } else {
- log_user 1
- }
- #
- # Done configuration/variable setting. Now run with it...
- #
- # Sets Xterm title if interactive...if its an xterm and the user cares
- proc label { host } {
- global env
- # if CLOGIN has an 'x' in it, don't set the xterm name/banner
- if [info exists env(CLOGIN)] {
- if {[string first "x" $env(CLOGIN)] != -1} { return }
- }
- # take host from ENV(TERM)
- if [info exists env(TERM)] {
- if [regexp \^(xterm|vs) $env(TERM) ignore ] {
- send_user "\033]1;[lindex [split $host "."] 0]\a"
- send_user "\033]2;$host\a"
- }
- }
- }
- # This is a helper function to make the password file easier to
- # maintain. Using this the password file has the form:
- # add password sl* pete cow
- # add password at* steve
- # add password * hanky-pie
- proc add {var args} { global int_$var ; lappend int_$var $args}
- proc include {args} {
- global env
- regsub -all "(^{|}$)" $args {} args
- if { [ regexp "^/" $args ignore ] == 0 } {
- set args $env(HOME)/$args
- }
- source_password_file $args
- }
- proc find {var router} {
- upvar int_$var list
- if { [info exists list] } {
- foreach line $list {
- if { [string match [lindex $line 0] $router ] } {
- return [lrange $line 1 end]
- }
- }
- }
- return {}
- }
- # Loads the password file. Note that as this file is tcl, and that
- # it is sourced, the user better know what to put in there, as it
- # could install more than just password info... I will assume however,
- # that a "bad guy" could just as easy put such code in the clogin
- # script, so I will leave .cloginrc as just an extention of that script
- proc source_password_file { password_file } {
- global env
- if { ! [file exists $password_file] } {
- send_user "\nError: password file ($password_file) does not exist\n"
- exit 1
- }
- file stat $password_file fileinfo
- if { [expr ($fileinfo(mode) & 007)] != 0000 } {
- send_user "\nError: $password_file must not be world readable/writable\n"
- exit 1
- }
- if [ catch {source $password_file} reason ] {
- send_user "\nError: $reason\n"
- exit 1
- }
- }
- # Log into the router.
- # returns: 0 on success, 1 on failure
- proc login { router user userpswd passwd enapasswd prompt cmethod cyphertype } {
- global spawn_id in_proc do_command do_script
- global u_prompt p_prompt e_prompt sshcmd
- set in_proc 1
- # try each of the connection methods in $cmethod until one is successful
- set progs [llength $cmethod]
- foreach prog [lrange $cmethod 0 end] {
- incr progs -1
- if [string match "telnet*" $prog] {
- regexp {telnet(:([^[:space:]]+))*} $prog command suffix port
- if {"$port" == ""} {
- set retval [ catch {spawn telnet $router} reason ]
- } else {
- set retval [ catch {spawn telnet $router $port} reason ]
- }
- if { $retval } {
- send_user "\nError: telnet failed: $reason\n"
- return 1
- }
- } elseif ![string compare $prog "ssh"] {
- if [ catch {spawn $sshcmd -c $cyphertype -x -l $user $router} reason ] {
- send_user "\nError: $sshcmd failed: $reason\n"
- return 1
- }
- } elseif ![string compare $prog "rsh"] {
- send_error "\nError: unsupported method: rsh\n"
- if { $progs == 0 } {
- return 1
- }
- continue;
- } else {
- send_user "\nError: unknown connection method: $prog\n"
- return 1
- }
- sleep 0.3
- # This helps cleanup each expect clause.
- expect_after {
- timeout {
- send_user "\nError: TIMEOUT reached\n"
- catch {close}; catch {wait};
- if { $in_proc} {
- return 1
- } else {
- continue
- }
- } eof {
- send_user "\nError: EOF received\n"
- catch {close}; catch {wait};
- if { $in_proc} {
- return 1
- } else {
- continue
- }
- }
- }
- # Here we get a little tricky. There are several possibilities:
- # the router can ask for a username and passwd and then
- # talk to the TACACS server to authenticate you, or if the
- # TACACS server is not working, then it will use the enable
- # passwd. Or, the router might not have TACACS turned on,
- # then it will just send the passwd.
- # if telnet fails with connection refused, try ssh
- expect {
- -re "(Connection refused|Secure connection \[^\n\r]+ refused|Connection closed by)" {
- catch {close}; catch {wait};
- if !$progs {
- send_user "\nError: Connection Refused ($prog)\n"; return 1
- }
- }
- eof { send_user "\nError: Couldn't login\n"; wait; return 1
- } -nocase "unknown host\r" {
- catch {close}; catch {wait};
- send_user "\nError: Unknown host\n"; wait; return 1
- } "Host is unreachable" {
- catch {close}; catch {wait};
- send_user "\nError: Host Unreachable!\n"; wait; return 1
- } "No address associated with name" {
- catch {close}; catch {wait};
- send_user "\nError: Unknown host\n"; wait; return 1
- }
- -re "(Host key not found |The authenticity of host .* be established).*\(yes\/no\)\?" {
- send "yes\r"
- send_user "\nHost $router added to the list of known hosts.\n"
- exp_continue }
- -re "HOST IDENTIFICATION HAS CHANGED.* \(yes\/no\)\?" {
- send "no\r"
- send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n"
- return 1
- }
- -re "Offending key for .* \(yes\/no\)\?" {
- send "no\r"
- send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n"
- return 1
- }
- -re "Ctrl-Y" { send -- "\031"
- expect {
- -re "$u_prompt" { send -- "$user\r" ; send -- "$userpswd\r" }
- -re "#" { set in_proc 0; return 0 }
- }
- # exp_continue
- }
- -re "$u_prompt" { send -- "$user\r"
- expect {
- eof { send_user "\nError: Couldn't login\n"; wait; return 1 }
- "Login invalid" { send_user "\nError: Invalid login\n";
- catch {close}; catch {wait};
- return 1 }
- -re "$p_prompt" { send -- "$userpswd\r" }
- "$prompt" { set in_proc 0; return 0 }
- }
- exp_continue
- }
- -re "$p_prompt" {
- if $in_proc { exp_continue }
- if ![string compare $prog "ssh"] {
- send -- "$userpswd\r"
- } else {
- send -- "$passwd\r"
- }
- expect {
- eof { send_user "\nError: Couldn't login\n"; wait; return 1 }
- -re "$e_prompt" { send -- "$enapasswd\r" }
- "$prompt" { set in_proc 0; return 0 }
- }
- exp_continue
- }
- #"$prompt" { break; }
- denied { send_user "\nError: Check your passwd for $router\n"
- catch {close}; catch {wait}; return 1
- }
- "% Bad passwords" {send_user "\nError: Check your passwd for $router\n"; return 1 }
- }
- }
- set in_proc 0
- return 0
- }
- # Enable
- proc do_enable { enauser enapasswd } {
- global prompt in_proc
- global u_prompt e_prompt
- set in_proc 1
- send "enable\r"
- expect {
- -re "$u_prompt" { send -- "$enauser\r"; exp_continue}
- -re "$e_prompt" { send -- "$enapasswd\r"; exp_continue}
- "#" { set prompt "#" }
- "(enable)" { set prompt "> (enable) " }
- denied { send_user "\nError: Check your Enable passwd\n"
- return 1
- }
- "% Bad passwords" {
- send_user "\nError: Check your Enable passwd\n"
- return 1
- }
- }
- # We set the prompt variable (above) so script files don't need
- # to know what it is.
- set in_proc 0
- return 0
- }
- # Run commands given on the command line.
- proc run_commands { prompt command } {
- global in_proc
- set in_proc 1
- regsub -all "\[)(]" $prompt {\\&} reprompt
- set commands [split $command \;]
- set num_commands [llength $commands]
- for {set i 0} {$i < $num_commands} { incr i} {
- send -- "[subst -nocommands [lindex $commands $i]]\r"
- expect {
- -re "^\[^\n\r *]*$reprompt" {}
- -re "^\[^\n\r]*$reprompt." { exp_continue }
- -re "\[\n\r]+" { exp_continue }
- -re "Main Menu" { send "L" }
- }
- }
- expect {
- "\n" { exp_continue }
- timeout { catch {close}; catch {wait};
- return 0
- }
- eof { return 0 }
- }
- set in_proc 0
- }
- #
- # For each router... (this is main loop)
- #
- source_password_file $password_file
- set in_proc 0
- set exitval 0
- foreach router [lrange $argv $i end] {
- set router [string tolower $router]
- send_user "$router\n"
- # Figure out prompt.
- # Since autoenable is off by default, if we have it defined, it
- # was done on the command line. If it is not specifically set on the
- # command line, check the password file.
- if $avautoenable {
- set autoenable 1
- set enable 0
- set prompt "#"
- } else {
- set ae [find autoenable $router]
- if { "$ae" == "1" } {
- set autoenable 1
- set enable 0
- set prompt "#"
- } else {
- set autoenable 0
- set enable $avenable
- set prompt ">"
- }
- }
- # look for noenable option in .cloginrc
- if { [find noenable $router] != "" } {
- set enable 0
- }
- # Figure out passwords
- if { $do_passwd || $do_enapasswd } {
- set pswd [find password $router]
- if { [llength $pswd] == 0 } {
- send_user "\nError - no password for $router in $password_file.\n"
- continue
- }
- if { $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } {
- send_user "\nError - no enable password for $router in $password_file.\n"
- continue
- }
- set passwd [join [lindex $pswd 0] ""]
- set enapasswd [join [lindex $pswd 1] ""]
- } else {
- set passwd $userpasswd
- set enapasswd $enapasswd
- }
- # Figure out username
- if {[info exists username]} {
- # command line username
- set ruser $username
- } else {
- set ruser [join [find user $router] ""]
- if { "$ruser" == "" } { set ruser $default_user }
- }
- # Figure out username's password (if different from the vty password)
- if {[info exists userpasswd]} {
- # command line username
- set userpswd $userpasswd
- } else {
- set userpswd [join [find userpassword $router] ""]
- if { "$userpswd" == "" } { set userpswd $passwd }
- }
- # Figure out enable username
- if {[info exists enausername]} {
- # command line enausername
- set enauser $enausername
- } else {
- set enauser [join [find enauser $router] ""]
- if { "$enauser" == "" } { set enauser $ruser }
- }
- # Figure out prompts
- set u_prompt [find userprompt $router]
- if { "$u_prompt" == "" } {
- set u_prompt "(Username|login|user name):"
- } else {
- set u_prompt [join [lindex $u_prompt 0] ""]
- }
- set p_prompt [find passprompt $router]
- if { "$p_prompt" == "" } {
- set p_prompt "(\[Pp]assword|passwd):"
- } else {
- set p_prompt [join [lindex $p_prompt 0] ""]
- }
- set e_prompt [find enableprompt $router]
- if { "$e_prompt" == "" } {
- set e_prompt "\[Pp]assword:"
- } else {
- set e_prompt [join [lindex $e_prompt 0] ""]
- }
- # Figure out cypher type
- if {[info exists cypher]} {
- # command line cypher type
- set cyphertype $cypher
- } else {
- set cyphertype [find cyphertype $router]
- if { "$cyphertype" == "" } { set cyphertype "3des" }
- }
- # Figure out connection method
- set cmethod [find method $router]
- if { "$cmethod" == "" } { set cmethod {{telnet}} }
- # Figure out the SSH executable name
- set sshcmd [find sshcmd $router]
- if { "$sshcmd" == "" } { set sshcmd {ssh} }
- # Login to the router
- if {[login $router $ruser $userpswd $passwd $enapasswd $prompt $cmethod $cyphertype]} {
- incr exitval
- continue
- }
- if { $enable } {
- if {[do_enable $enauser $enapasswd]} {
- if { $do_command || $do_script } {
- incr exitval
- catch {close}; catch {wait};
- continue
- }
- }
- }
- if { $do_command } {
- if {[run_commands $prompt $command]} {
- incr exitval
- continue
- }
- } elseif { $do_script } {
- expect $prompt {}
- source $sfile
- catch {close};
- } else {
- label $router
- log_user 1
- interact
- }
- # End of for each router
- catch {wait};
- sleep 0.3
- }
- exit $exitval
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement