Problems with RE matching in .cloginrc
Dylan Hall
dylan.hall at tsnz.net
Mon Oct 15 22:59:36 UTC 2001
We're having some problems getting the .cloginrc file to do what we want. Below is an extract from the file (passwords changed). The idea is that we have some wildcard entries to catch most of our switches, but a couple of more specific matches where they differ.
#
# Edge Switches.....
#
add method s3-BNZW01-r01.engops.tsnz.net {ssh}
add user s3-BNZW01-r01.engops.tsnz.net rancid
add password s3-BNZW01-r01.engops.tsnz.net 12345
add method *-r01.engops.tsnz.net {ssh}
add user *-r01.engops.tsnz.net admin
add password *-r01.engops.tsnz.net abcde
When we use xlogin (a hack of clogin or jlogin to work with Extremes - I can't remember which) the following happens
% ./xlogin -c "show version" s3-BNZW01-r01.engops.tsnz.net
s3-bnzw01-r01.engops.tsnz.net
spawn ssh -c 3des -x -l admin s3-bnzw01-r01.engops.tsnz.net
xlogin seems to be getting the wrong username.
if I change the case of entries in .cloginrc so everything is lowercase as follows:
#
# Edge Switches.....
#
add method s3-bnzw01-r01.engops.tsnz.net {ssh}
add user s3-bnzw01-r01.engops.tsnz.net rancid
add password s3-bnzw01-r01.engops.tsnz.net 12345
add method *-r01.engops.tsnz.net {ssh}
add user *-r01.engops.tsnz.net admin
add password *-r01.engops.tsnz.net abcde
% ./xlogin -c "show version" s3-bnzw01-r01.engops.tsnz.net
s3-bnzw01-r01.engops.tsnz.net
spawn ssh -c 3des -x -l rancid s3-bnzw01-r01.engops.tsnz.net
rancid at s3-bnzw01-r01.engops.tsnz.net's password:
Permission denied, please try again.
xlogin seems to get the right username now, but is still getting the wrong password.
Can anyone explain/fix this? Are we using wildcards in the wrong way?
I have attached xlogin, although the functions that match passwords are unaltered.
This problems exists on my linux box with expect version 5.31.8 and on my freebsd box with expect version 5.32.1
Thanks for your help,
--
Dylan Hall
IP Engineer
TelstraSaturn Ltd
Ph: +64 4 9395000 Fax: +64 4 9228555
-------------- next part --------------
#!/usr/local/bin/expect --
##
##
## Copyright (C) 1997 by Henry Kilmer, Erik Sherk and Pete Whiting.
## All rights reserved.
##
## This software may be freely copied, modified and redistributed without
## fee for non-commerical purposes provided that this copyright notice is
## preserved intact on all copies and modified copies.
##
## There is no warranty or other guarantee of fitness of this software.
## It is provided solely "as is". The author(s) disclaim(s) all
## responsibility and liability with respect to this software's usage
## or its effect upon hardware, computer systems, other software, or
## anything else.
##
##
#
# xlogin - extreme ssh 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 \[-c command\] \[-f cloginrc-file\] \
\[-p user-password\] \[-r passphrase\] \[-s script-file\] \[-v save\] \
\[-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 enable 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
# No passphrase by default
set passphrase ""
# Don't save changes by default
set saveyn "n"
# 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)
} 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 "Error: could not exec id: $reason\n"
exit 1
}
regexp {\(([^)]*)} "$reason" junk default_user
}
# 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
# alternate cloginrc file
} -f* -
-F* {
if {! [ regexp .\[fF\](.+) $arg ignore password_file]} {
incr i
set password_file [ lindex $argv $i ]
}
# user Password
} -p* -
-P* {
if {! [ regexp .\[pP\](.+) $arg ignore userpswd]} {
incr i
set userpswd [ lindex $argv $i ]
}
set do_passwd 0
# passphrase
} -r* -
-R* {
if {! [ regexp .\[rR\](.+) $arg ignore passphrase]} {
incr i
set passphrase [ lindex $argv $i ]
}
# Expect script to run.
} -s* -
-S* {
if {! [ regexp .\[sS\](.+) $arg ignore sfile]} {
incr i
set sfile [ lindex $argv $i ]
}
if { ! [ file readable $sfile ] } {
send_user "Error: Can't read $sfile\n"
exit 1
}
set do_script 1
# Timeout
} -t* -
-T* {
if {! [ regexp .\[tT\](.+) $arg ignore timeout]} {
incr i
set timeout [ lindex $argv $i ]
}
# Username
} -u* -
-U* {
if {! [ regexp .\[uU\](.+) $arg ignore user]} {
incr i
set username [ lindex $argv $i ]
}
# Save changes
} -v* -
-V* {
if {! [ regexp .\[vV\](.+) $arg ignore saveyn]} {
incr i
set saveyn [ lindex $argv $i ]
}
# command file
} -x* -
-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
# 'ssh -c' cypher type
} -y* -
-Y* {
if {! [ regexp .\[yY\](.+) $arg ignore cypher]} {
incr i
set cypher [ lindex $argv $i ]
}
} -* {
send_user "Error: Unknown argument! $arg\n"
send_user $usage
exit 1
} default {
break
}
}
}
# Process routers...no routers listed is an error.
if { $i == $argc } {
send_user "Error: $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 "Error: password file ($password_file) does not exist\n"
exit 1
}
file stat $password_file fileinfo
if { [expr ($fileinfo(mode) & 007)] != 0000 } {
send_user "Error: $password_file must not be world readable/writable\n"
exit 1
}
if [ catch {source $password_file} reason ] {
send_user "Error: $reason\n"
exit 1
}
}
# Log into the router.
proc login { router user passwd prompt cmethod cyphertype identfile} {
global spawn_id in_proc do_command do_script passphrase
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] {
if ![string compare $prog "telnet"] {
if [ catch {spawn telnet $router} reason ] {
send_user "Error: telnet failed: $reason\n"
exit 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 ssh -c $cyphertype -x -l $user -i $identfile $router} reason ] {
send_user "Error: failed to ssh: $reason\n"
exit 1
}
} else {
if [ catch {spawn ssh -c $cyphertype -x -l $user $router} reason ] {
send_user "Error: failed to ssh: $reason\n"
exit 1
}
}
} elseif ![string compare $prog "rsh"] {
if [ catch {spawn rsh -l $user $router} reason ] {
send_user "Error: rsh failed: $reason\n"
exit 1
}
} else {
puts "ERROR: unknown connection method: $prog"
return 1
}
incr progs -1
sleep 0.3
# This helps cleanup each expect clause.
expect_after {
timeout {
send_user "\nError: TIMEOUT reached\n"
catch {close}; wait
if { $in_proc} {
return 1
} else {
continue
}
} eof {
send_user "\nError: EOF received\n"
catch {close}; 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|Connectionclosed by)" {
catch {close}; wait
if !$progs {
send_user "\nError: Connection Refused ($prog)\n"; return 1
}
}
eof { send_user "Error: Couldn't login\n"; wait; return 1
} -nocase "unknown host\r\n" {
catch {close};
send_user "Error: Unknown host\n"; wait; return 1
} "Host is unreachable" {
catch {close};
send_user "Error: Host Unreachable!\n"; wait; return 1
} "No address associated with name" {
catch {close};
send_user "Error: Unknown host\n"; wait; return 1
}
-re "Enter passphrase for RSA key '\[^'\]*': " {
send_user "\nKey has passphrase!\n"
send "$passphrase\r"
exp_continue }
-re "Host key not found .* \(yes\/no\)\?" {
send "yes\r"
send_user "Host $router added to the list of known hosts.\n"
exp_continue }
-re "HOST IDENTIFICATION HAS CHANGED.* \(yes\/no\)\?" {
send "no\r"
send_user "Error: The host key for $router has changed. update the known_hosts file accordingly.\n"
return 1 }
-re "(Username|\[\r\n]login):" { send "$user\r"
expect {
eof { send_user "Error: Couldn't login\n";
wait; return 1 }
-re "\[Pp]assword:" { send "$passwd\r" }
"$prompt" { set in_proc 0; return 0 }
}
exp_continue
}
"\[Pp]assword:" { send "$passwd\r"
expect {
eof { send_user "Error: Couldn't login\n"; wait; return 1 }
"$prompt" { set in_proc 0; return 0 }
}
exp_continue
}
"$prompt" { break; }
denied { send_user "Error: Check your passwd for $router\n"
if { $do_command || $do_script } {
send "quit"
wait
return 1
} else {
return 1
}
}
"% Bad passwords" {send_user "Error: Check your passwd for $router\n"; return 1 }
}
}
set in_proc 0
return 0
}
# Run commands given on the command line.
proc run_commands { prompt command saveyn } {
global in_proc
set in_proc 1
send "disable clipaging\r"
expect $prompt {}
# send "set cli screen-length 0\r"
# expect $prompt {}
# Is this a multi-command?
if [ string match "*\;*" "$command" ] {
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 }
}
}
} else {
send "$command\r"
expect {
-re "^\[^\n\r]*$prompt $" {}
-re "^\[^\n\r]*$prompt." { exp_continue }
-re "(\r\n|\n)" { exp_continue }
}
}
send "enable clipaging\r"
expect $prompt {}
send "exit\r"
expect {
"Do you wish to save your configuration changes? (y/n)" {
send "$saveyn\r"
exp_continue
}
"\n" { exp_continue }
timeout { 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
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 [find user $router]
if { "$loginname" == "" } { set loginname $default_user }
}
# Figure out loginname's password (if different from the vty password)
if {[info exists userpswd]} {
# command line passwd
set passwd $userpswd
} else {
set passwd [lindex [find password $loginname@$router] 0]
if { "$passwd" == "" } { set passwd [lindex [find password $router] 0] }
}
# figure out identity file to use
set identfile ""
if {[info exists identity]} {
set identfile [lindex [find identity $router] 0]
}
# 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}} }
# Login to the router
if {[login $router $loginname $passwd $prompt $cmethod $cyphertype $identfile]} {
continue
}
if { $do_command } {
if {[run_commands $prompt $command $saveyn]} {
continue
}
} elseif { $do_script } {
send "disable clipaging\r"
expect $prompt {}
# send "set cli screen-length 0\r"
# expect $prompt {}
source $sfile
close
} else {
label $router
log_user 1
interact
}
# End of for each router
wait
sleep 0.3
}
exit 0
More information about the Rancid-discuss
mailing list