Problems with RE matching in .cloginrc

john heasley heas at shrubbery.net
Tue Oct 16 03:31:15 UTC 2001


this is for extreme switches?  if so, please try clogin in 2.2b8.

Tue, Oct 16, 2001 at 11:59:36AM +1300, Dylan Hall:
> 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
> #!/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