# Usage line set usage "Usage: $argv0 \[-dhiSV\] \[-m|M\] \[-autoenable\] \[-noenable\] \ \[-c command\] \[-Evar=x\] \[-e enable-password\] \[-f cloginrc-file\] \ \[-p user-password\] \[-r passphrase\] \[-s script-file\] \[-t timeout\] \ \[-u username\] \[-v vty-password\] \[-w enable-username\] \[-x command-file\] \ \[-y ssh_cypher_type\] router \[router...\]\n" # Password file set password_file $env(HOME)/.cloginrc # Default is to login to the router set do_command 0 set do_interact 0 set do_script 0 # The default is to automatically enable if {! [info exists avenable]} { set avenable 1 } # 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 1 # Save config, if prompted set do_saveconfig 0 # cloginrc debugging knob set do_cloginrcdbg 0 # Sometimes routers take awhile to answer (the default is 10 sec) if {! [info exists timeoutdflt]} { set timeoutdflt 10 } # spawn tty options set spawnopts {} # intialize cloginrc parsing stacks set int_file {} set int_lineno {} # env(CLOGIN) may contain: # x == do not set xterm banner or name # Find the user in the ENV, or use the unix userid. if {![info exists default_user] && [info exists env(CISCO_USER)]} { if {[string length $env(CISCO_USER)]} { set default_user $env(CISCO_USER) } } if {![info exists default_user] && [info exists env(USER)]} { if {[string length $env(USER)]} { set default_user $env(USER) } } if {![info exists default_user] && [info exists env(LOGNAME)]} { if {[string length $env(LOGNAME)]} { set default_user $env(LOGNAME) } } if (![info exists default_user]) { # 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) } # 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 # Help } -h* { send_user "$usage" exit 0 # Command to run. } -c* { if {! [regexp .\[cC\](.+) $arg ignore command]} { incr i set command [lindex $argv $i] } set do_command 1 # Enable Password } -e* { if {! [regexp .\[e\](.+) $arg ignore enapasswd]} { incr i set enapasswd [lindex $argv $i] } set do_enapasswd 0 # 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] } # interactive } -i* { set do_interact 1 # user Password } -p* { if {! [regexp .\[pP\](.+) $arg ignore userpasswd]} { incr i set userpasswd [lindex $argv $i] } set do_passwd 0 # cloginrc debugging knobs } -m* { set do_cloginrcdbg 1 } -M* { set do_cloginrcdbg 2 # ssh 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* { # may not be supported by the script and may not be applicable to # the platform set do_saveconfig 1 # Timeout } -t* { if {! [regexp .\[tT\](.+) $arg ignore timeout]} { incr i set timeoutdflt [lindex $argv $i] } # Username } -u* { if {! [regexp .\[uU\](.+) $arg ignore user]} { incr i set username [lindex $argv $i] } # VTY Password } -v* { # some scripts ignore -v, like jlogin 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* { # may not be supported by the script and may not be applicable to # the platform if {! [regexp .\[wW\](.+) $arg ignore enauser]} { incr i set enausername [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 .\[eE\](.+) $arg ignore cypher]} { incr i set cypher [lindex $argv $i] } # 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_file int_lineno int_$var set file [lindex $int_file 0] set lineno [lindex $int_lineno 0] lappend int_$var "$var:$file:$lineno: $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} { global do_cloginrcdbg upvar int_$var list if {[info exists list]} { foreach line $list { if {[string match -nocase [lindex $line 1] $router]} { if {$do_cloginrcdbg > 0} { send_error -- [join [list [lindex $line 0] [lrange $line 1 end] "\r\n"]] } if {$do_cloginrcdbg == 2} { # save return value if {! [info exists result]} { set result [lrange $line 2 end] } } else { return [lrange $line 2 end] } } } } if {$do_cloginrcdbg == 2} { if {[info exists result]} { return $result } } 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 {file} { global env int_file int_lineno if {! [file exists $file]} { send_user "\nError: password file ($file) does not exist\n" exit 1 } file stat $file fileinfo if {[expr ($fileinfo(mode) & 007)] != 0000} { send_user "\nError: $file must not be world readable/writable\n" exit 1 } if [catch {set fd [open $file "r"]} reason] { send_user "\nError: $reason\n" exit 1 } set int_file [linsert $int_file 0 $file] set int_lineno [linsert $int_lineno 0 0] while {[gets $fd line] >= 0} { set tmp [lindex $int_lineno 0]; incr tmp lset int_lineno 0 $tmp eval $line } set int_file [lrange $int_file 1 end] set int_lineno [lrange $int_lineno 1 end] close $fd }