Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /usr/lib/rancid/bin$ cat jlogin
- #! /usr/bin/expect --
- ##
- ## $Id: jlogin.in,v 1.69 2009/04/16 21:22:57 heas Exp $
- ##
- ## rancid 2.3.2
- ## 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.
- #
- # jlogin - juniper login
- #
- # Most options are intuitive for logging into a Cisco router.
- # The default username password is the same as the vty password.
- #
- # Usage line
- set usage "Usage: $argv0 \[-dSV\] \[-c command\] \[-Evar=x\] \
- \[-f cloginrc-file\] \[-p user-password\] \[-r passphrase\] \[-s script-file\] \
- \[-u username\] \[-t timeout\] \[-x command-file\] \[-y ssh_cypher_type\] \
- router \[router...\]\n"
- # env(CLOGIN) may contain the following chars:
- # 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 1
- # 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
- # 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 120
- # Process the command line
- for {set i 0} {$i < $argc} {incr i} {
- set arg [lindex $argv $i]
- switch -glob -- $arg {
- # Command to run.
- -c* -
- -C* {
- if {! [ regexp .\[cC\](.+) $arg ignore command]} {
- incr i
- set command [ lindex $argv $i ]
- }
- set do_command 1
- # Expect debug mode
- } -d* {
- exp_internal 1
- # 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
- }
- # alternate cloginrc file
- } -f* {
- if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
- incr i
- set password_file [ lindex $argv $i ]
- }
- # user Password
- } -p* {
- if {! [ regexp .\[pP\](.+) $arg ignore userpasswd]} {
- incr i
- set userpasswd [ lindex $argv $i ]
- }
- set do_passwd 0
- # Version string
- } -V* {
- send_user "rancid 2.3.2\n"
- exit 0
- # passphrase
- } -r* {
- if {! [ regexp .\[rR\](.+) $arg ignore passphrase]} {
- incr i
- set avpassphrase [ lindex $argv $i ]
- }
- # 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
- # Timeout
- } -t* {
- if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
- incr i
- set timeout [ lindex $argv $i ]
- }
- # Username
- } -u* {
- if {! [ regexp .\[uU\](.+) $arg ignore user]} {
- incr i
- set username [ lindex $argv $i ]
- }
- # command file
- } -x* {
- if {! [ regexp .\[xX\](.+) $arg ignore cmd_file]} {
- incr i
- set cmd_file [ lindex $argv $i ]
- }
- if [ catch {set cmd_fd [open $cmd_file r]} reason ] {
- send_user "\nError: $reason\n"
- exit 1
- }
- set cmd_text [read $cmd_fd]
- close $cmd_fd
- set command [join [split $cmd_text \n] \;]
- set do_command 1
- # 'ssh -c' cypher type
- } -y* {
- if {! [ regexp .\[yY\](.+) $arg ignore cypher]} {
- incr i
- set cypher [ lindex $argv $i ]
- }
- } -* {
- 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 passwd cmethod cyphertype identfile} {
- global spawn_id in_proc do_command do_script passphrase prompt
- global 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"] {
- # ssh to the router & try to login with or without an identfile.
- # We use two calls to spawn since spawn does not seem to parse
- # spaces correctly.
- if {$identfile != ""} {
- if [ catch {spawn $sshcmd -c $cyphertype -x -l $user -i $identfile $router} reason ] {
- send_user "\nError: failed to $sshcmd: $reason\n"
- return 1
- }
- } else {
- if [ catch {spawn $sshcmd -c $cyphertype -x -l $user $router} reason ] {
- send_user "\nError: failed to $sshcmd: $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.
- 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\n" {
- 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
- }
- "Login incorrect" {
- send_user "\nError: Check your password for $router\n"
- catch {close}; catch {wait}; return 1
- }
- -re "Enter passphrase.*: " {
- # sleep briefly to allow time for stty -echo
- sleep 1
- send -- "$passphrase\r"
- exp_continue }
- -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 "(Username|\[\r\n]login):" {
- send -- "$user\r"
- exp_continue
- }
- "\[Pp]assword:" {
- sleep 1; send -- "$passwd\r"
- exp_continue
- }
- -re "$prompt" { break; }
- denied { send_user "\nError: Check your password for $router\n"
- catch {close}; catch {wait}; return 1
- }
- }
- }
- # we are logged in, now figure out the full prompt
- send "\r"
- expect {
- -re "(\r\n|\n)" { exp_continue; }
- -re "^\[^ ]+$prompt" { set prompt $expect_out(0,string);
- regsub ">" $prompt "\[#>]" prompt;
- }
- }
- 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
- send "set cli complete-on-space off\r"
- expect -re $prompt {}
- send "set cli screen-length 0\r"
- expect -re $prompt {}
- set commands [split $command \;]
- set num_commands [llength $commands]
- for {set i 0} {$i < $num_commands} { incr i} {
- send "[lindex $commands $i]\r"
- expect {
- -re "^\[^\n\r *]*$prompt $" {}
- -re "^\[^\n\r]*$prompt." { exp_continue }
- -re "(\r\n|\n)" { exp_continue }
- }
- }
- send "quit\r"
- 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"
- set prompt ">"
- # Figure out username
- if {[info exists username]} {
- # command line username
- set loginname $username
- } else {
- set loginname [join [find user $router] ""]
- if { "$loginname" == "" } { set loginname $default_user }
- }
- # Figure out loginname's password (if different from the vty password)
- if {[info exists userpasswd]} {
- # command line passwd
- set passwd $userpasswd
- } else {
- set passwd [join [lindex [find userpassword $router] 0] ""]
- if { "$passwd" == "" } {
- set passwd [join [lindex [find password $router] 0] ""]
- if { "$passwd" == "" } {
- send_user "\nError: no password for $router in $password_file.\n"
- continue
- }
- }
- }
- # Figure out identity file to use
- set identfile [join [lindex [find identity $router] 0] ""]
- # Figure out passphrase to use
- if {[info exists avpassphrase]} {
- set passphrase $avpassphrase
- } else {
- set passphrase [join [lindex [find passphrase $router] 0] ""]
- }
- if { ! [string length "$passphrase"]} {
- set passphrase $passwd
- }
- # Figure out ssh cypher type
- if {[info exists cypher]} {
- # command line ssh 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} {ssh}} }
- # Figure out the SSH executable name
- set sshcmd [find sshcmd $router]
- if { "$sshcmd" == "" } { set sshcmd {ssh} }
- # Login to the router
- if {[login $router $loginname $passwd $cmethod $cyphertype $identfile]} {
- incr exitval
- continue
- }
- if { $do_command } {
- if {[run_commands $prompt $command]} {
- incr exitval
- continue
- }
- } elseif { $do_script } {
- send "set cli complete-on-space off\r"
- expect -re $prompt {}
- send "set cli screen-length 0\r"
- expect -re $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