#! @EXPECT_PATH@ -- ## ## @PACKAGE@ @VERSION@ @copyright@ # # ulogin - unix login; EdgeRouter, ... # # Most options are intuitive for logging into a Cisco router. # The default is to enable (thus -noenable). Some folks have # setup tacacs to have a user login at priv-lvl = 15 (enabled) # so the -autoenable flag was added for this case (don't go through # the process of enabling and the prompt will be the "#" prompt. # The default username password is the same as the vty password. # # Sometimes routers take awhile to answer (the default is 10 sec) set timeoutdflt 30 # Some CLIs having problems if we write too fast set send_human {.2 .1 .4 .2 1} @login_top@ # Log into the router. # returns: 0 on success, 1 on failure proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } { global spawn_id in_proc do_command do_script passphrase global prompt u_prompt p_prompt e_prompt sshcmd telnetcmd 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 regexp {(telnet|ssh)(:([^[:space:]]+))*} $prog command suffix junk port if [string match "telnet*" $prog] { if {"$port" == ""} { if { $do_command || $do_script } { set retval [ catch {eval spawn [split "hpuifilter -- $telnetcmd $router"]} reason ] } else { set retval [ catch {eval spawn [split "$telnetcmd $router"]} reason ] } } else { if { $do_command || $do_script } { set retval [ catch {eval spawn [split "hpuifilter -- $telnetcmd $router $port"]} reason ] } else { set retval [ catch {eval spawn [split "$telnetcmd $router $port"]} reason ] } } if { $retval } { send_user "\nError: telnet failed: $reason\n" return 1 } } elseif [string match "ssh*" $prog] { # ssh to the router & try to login with or without an identfile. regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port set cmd $sshcmd if {"$port" != ""} { set cmd "$cmd -p $port" } if {"$cyphertype" != ""} { set cmd "$cmd -c $cyphertype" } if {"$identfile" != ""} { set cmd "$cmd -i $identfile" } if { $do_command || $do_script } { set retval [ catch {eval spawn hpuifilter -- [split "$cmd -x -l $user $router" { }]} reason ] } else { set retval [ catch {eval spawn [split "$cmd -x -l $user $router" { }]} reason ] } if { $retval } { send_user "\nError: $cmd 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 { global in_proc send_user "\nError: TIMEOUT reached\n" catch {close}; catch {wait}; if {$in_proc} { return 1 } else { continue } } eof { global in_proc 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 { "Press any key to continue" { send " " exp_continue } "Enter switch number to connect to or :" { send "\r" exp_continue } -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 } } "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)" { expect { -re "\\(yes\/no\[^\\)]*\\)\\?" { send "yes\r"; send_user "\nHost $router added to the list of known hosts.\n" } -re "\[^\r\n]*\[\r\n]+" { exp_continue; } } exp_continue } -re "HOST IDENTIFICATION HAS CHANGED" { send_user "\nError: The host key for $router has changed. Update the SSH known_hosts file accordingly.\n" expect { -re "\\(yes\/no\\)\\?" { send "no\r" } -re " strict checking\.\[\r\n]+" { } -re "\[^\r\n]*\[\r\n]+" { exp_continue; } } catch {close}; catch {wait}; return 1 } -re "Offending key for " { send_user "\nError: host key mismatch for $router. Update the SSH known_hosts file accordingly.\n" expect { -re "\\(yes\/no\\)\\?" { send "no\r" } -re "\[^\r\n]*\[\r\n]+" { exp_continue; } } catch {close}; catch {wait}; return 1 } -nocase -re "^warning: remote host denied authentication agent forwarding." { exp_continue; } -nocase -re "last login:" { exp_continue } -nocase -re "failed login:" { exp_continue } 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 } -re "Enter passphrase.*: " { # sleep briefly to allow time for stty -echo sleep 1 send -- "$passphrase\r" exp_continue } -nocase -re "last login:" { 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" } -re "$prompt" { set in_proc 0; return 0 } -re "\[^\r\n]*\[\r\n]+" { exp_continue; } } exp_continue } -re "$p_prompt" { 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" } -re "$prompt" { set in_proc 0; return 0 } -re "\[^\r\n]*\[\r\n]+" { exp_continue; } } exp_continue } -re "\[^\r\n]*\[\r\n]+" { exp_continue; } -re "$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 do_interact do_saveconfig in_proc set in_proc 1 # Turn off the pager and escape regex meta characters in the $prompt send "\r" # match cisco config mode prompts too, such as router(config-if)# regsub -all {^(.{1,11}).*([#$] ?)$} $prompt {\1} reprompt # escape any parens in the prompt, such as "(enable)" regsub -all {[)(]} $reprompt {\\&} reprompt append reprompt {([^#$\r\n]+)?[#$](\\([^)\\r\\n]+\\))?} expect { -re $reprompt {} -re "\[\n\r]+" { exp_continue } } # this is the only way i see to get rid of more prompts in o/p..grrrrr log_user 0 # handle escaped ;s in commands, and ;; and ^; regsub -all {([^\\]);} $command "\\1\u0002;" esccommand regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command regsub {^;} $command "\u0002;" esccommand regsub -all {[\\];} $esccommand ";" command regsub -all {\u0002;} $command "\u0002" esccommand set sep "\u0002" set commands [split $esccommand $sep] set num_commands [llength $commands] # if the pager can not be turned off, we have to look for the "More" # prompt. for {set i 0} {$i < $num_commands} { incr i} { send -- "[subst -nocommands [lindex $commands $i]]\r" expect { -re "^\[^\n\r *]*$reprompt" { catch {send_user -- "$expect_out(buffer)"} } -re "^\[^\n\r]*$reprompt " { catch {send_user -- "$expect_out(buffer)"} } -re "\[\n\r]+" { catch {send_user -- "$expect_out(buffer)"} exp_continue } -re "^<-+ More -+>\[^\n\r]*" { catch {send " "} exp_continue } -re "^-+ MORE -+\[^\n\r]*" { catch {send " "} exp_continue } # 3 flavours of the more prompt, -- first -More-, then --More-- (for # cisco/riverhead AGM), then with more dashes. -re "^-More-\[^\n\r-]*" { catch {send " "} exp_continue } -re "^--More--\[^\n\r-]*" { catch {send " "} exp_continue } -re "^---+More---+\[^\n\r]*" { catch {send " "} exp_continue } -re "\b+" { exp_continue } } } log_user 1 if { $do_interact == 1 } { interact return 0 } send -h "logout\r" expect { "Do you want to log out" { catch {send "y\r"} exp_continue } -re "\[\r\n]+" { exp_continue } -re "^.+>" { catch {send -h "exit\r"} 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 # if we have dont have a tty, we need some additional terminal settings if [catch {open /dev/tty w} ttyid] { # no tty, ie: cron set spawnopts "-nottycopy" set stty_init "cols 132" } else { catch {close ttyid} reason } foreach router [lrange $argv $i end] { set router [string tolower $router] send_user "$router\n" # device timeout set timeout [find timeout $router] if { [llength $timeout] == 0 } { set timeout $timeoutdflt } # Default prompt. set prompt [join [find prompt $router] ""] if { [llength $prompt] == 0 } { set prompt "\[^ ]\[$#] ?" } # look for autoenable option in .cloginrc & cmd-line set ae [find autoenable $router] if { "$ae" == "1" || $avautoenable } { set autoenable 1 } else { set autoenable 0 } # look for enable options in .cloginrc & cmd-line if { $avenable == 0 } { set enable 0 } else { set ne [find noenable $router] if { "$ne" == "1" || "$autoenable" == "1" } { set enable 0 } else { set enable 1 } } # 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 { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } { send_user -- "\nError: no enable password for $router in $password_file.\n" continue } if { $do_passwd } { set passwd [join [lindex $pswd 0] ""] } else { set passwd $userpasswd } if { $do_enapasswd } { set enapasswd [join [lindex $pswd 1] ""] } else { set enapasswd $enapasswd } } 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 "(\[Uu]sername|\[Ll]ogin|user name|Login 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 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 cypher type if {[info exists cypher]} { # command line cypher type set cyphertype $cypher } else { set cyphertype [find cyphertype $router] } # Figure out connection method set cmethod [find method $router] if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} } # Figure out the SSH executable name set sshcmd [join [lindex [find sshcmd $router] 0] ""] if { "$sshcmd" == "" } { set sshcmd {ssh} } # Figure out the telnet executable name set telnetcmd [join [lindex [find telnetcmd $router] 0] ""] if { "$telnetcmd" == "" } { set telnetcmd "@TELNET_CMD@" } # if [-mM], skip do not login if { $do_cloginrcdbg > 0 } { continue; } # Adjust our path to find hpuifilter set hpf_path "" regexp {(.*)/[^/]+} $argv0 junk hpf_path if { "$hpf_path" != "" && "$hpf_path" != "." } { append env(PATH) ":$hpf_path" } # Login to the router if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} { incr exitval continue } if { $enable } { if {[do_enable $enauser $enapasswd]} { if { $do_command || $do_script } { incr exitval catch {close}; catch {wait}; continue } } } # we are logged in, now figure out the full prompt send "\r" expect { -re "\[\r\n]+" { exp_continue; } -re "^.+$prompt" { set prompt $expect_out(0,string); } } if { $do_command } { if {[run_commands $prompt $command]} { incr exitval continue } } elseif { $do_script } { source $sfile catch {close}; } else { label $router log_user 1 interact } # End of for each router catch {wait}; sleep 0.3 } exit $exitval