1#! @EXPECT_PATH@ --
2##
3## @PACKAGE@ @VERSION@
4@copyright@
5#
6# a10login - A10 login
7#
8
9# Sometimes routers take awhile to answer (the default is 10 sec)
10set timeoutdflt 45
11# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
12set send_human {.2 .1 .4 .2 1}
13
14@login_top@
15
16# Log into the router.
17# returns: 0 on success, 1 on failure, -1 if rsh was used successfully
18proc login { router user userpswd passwd enapasswd cmethod cyphertype identfile } {
19    global spawn_id in_proc passphrase
20    global prompt prompt_match u_prompt p_prompt sshcmd telnetcmd
21    set in_proc 1
22    set uprompt_seen 0
23
24    # try each of the connection methods in $cmethod until one is successful
25    set progs [llength $cmethod]
26    foreach prog [lrange $cmethod 0 end] {
27	incr progs -1
28	if [string match "telnet*" $prog] {
29	    regexp {telnet(:([^[:space:]]+))*} $prog methcmd suffix port
30	    if {"$port" == ""} {
31		set retval [catch {eval spawn [split "$telnetcmd $router"]} reason]
32	    } else {
33		set retval [catch {eval spawn [split "$telnetcmd $router $port"]} reason]
34	    }
35	    if { $retval } {
36		send_user "\nError: telnet failed: $reason\n"
37		return 1
38	    }
39	} elseif [string match "ssh*" $prog] {
40	    # ssh to the router & try to login with or without an identfile.
41	    regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port
42	    set cmd $sshcmd
43	    if {"$port" != ""} {
44		set cmd "$cmd -p $port"
45	    }
46	    if {"$cyphertype" != ""} {
47		set cmd "$cmd -c $cyphertype"
48	    }
49	    if {"$identfile" != ""} {
50		set cmd "$cmd -i $identfile"
51	    }
52	    set retval [catch {eval spawn [split "$cmd -x -l $user $router" { }]} reason]
53	    if { $retval } {
54		send_user "\nError: $cmd failed: $reason\n"
55		return 1
56	    }
57	} else {
58	    send_user "\nError: unknown connection method: $prog\n"
59	    return 1
60	}
61	sleep 0.3
62
63	# This helps cleanup each expect clause.
64	expect_after {
65	    timeout {
66		global in_proc
67		send_user "\nError: TIMEOUT reached\n"
68		catch {close}; catch {wait};
69		if {$in_proc} {
70		    return 1
71		} else {
72		    continue
73		}
74	    } eof {
75		global in_proc
76		send_user "\nError: EOF received\n"
77		catch {close}; catch {wait};
78		if {$in_proc} {
79		    return 1
80		} else {
81		    continue
82		}
83	    }
84	}
85
86    # Here we get a little tricky.  There are several possibilities:
87    # the router can ask for a username and passwd and then
88    # talk to the TACACS server to authenticate you, or if the
89    # TACACS server is not working, then it will use the enable
90    # passwd.  Or, the router might not have TACACS turned on,
91    # then it will just send the passwd.
92    # if telnet fails with connection refused, try ssh
93    expect {
94	-re "^<-+ More -+>\[^\n\r]*" {
95	    # ASA will use the pager for long banners
96	    send " ";
97	    exp_continue
98	}
99	-re "(Connection refused|Secure connection \[^\n\r]+ refused)" {
100	    catch {close}; catch {wait};
101	    if !$progs {
102		send_user "\nError: Connection Refused ($prog): $router\n"
103		return 1
104	    }
105	}
106	-re "(Connection closed by|Connection to \[^\n\r]+ closed)" {
107	    catch {close}; catch {wait};
108	    if !$progs {
109		send_user "\nError: Connection closed ($prog): $router\n"
110		return 1
111	    }
112	}
113	eof { send_user "\nError: Couldn't login: $router\n"; wait; return 1 }
114	-nocase "unknown host\r" {
115	    send_user "\nError: Unknown host $router\n";
116	    catch {close}; catch {wait};
117	    return 1
118	}
119	"Host is unreachable" {
120	    send_user "\nError: Host Unreachable: $router\n";
121	    catch {close}; catch {wait};
122	    return 1
123	}
124	"No address associated with name" {
125	    send_user "\nError: Unknown host $router\n";
126	    catch {close}; catch {wait};
127	    return 1
128	}
129	-re "(Host key not found |The authenticity of host .* be established)" {
130	    expect {
131		-re "\\(yes\/no\[^\\)]*\\)\\?" {
132					  send "yes\r";
133					  send_user "\nHost $router added to the list of known hosts.\n"
134					 }
135		-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
136	    }
137	    exp_continue
138	}
139	-re "HOST IDENTIFICATION HAS CHANGED" {
140	    send_user "\nError: The host key for $router has changed.  Update the SSH known_hosts file accordingly.\n"
141	    expect {
142		-re "\\(yes\/no\\)\\?"	{ send "no\r" }
143		-re " strict checking\.\[\r\n]+" { }
144		-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
145	    }
146	    catch {close}; catch {wait};
147	    return 1
148	}
149	-re "Offending key for " {
150	    send_user "\nError: host key mismatch for $router.  Update the SSH known_hosts file accordingly.\n"
151	    expect {
152		-re "\\(yes\/no\\)\\?"	{ send "no\r" }
153		-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
154	    }
155	    catch {close}; catch {wait};
156	    return 1
157	}
158	-nocase -re "^warning: remote host denied authentication agent forwarding." {
159	    exp_continue;
160	}
161	-re "(denied|Sorry)"	{
162				  send_user "\nError: Check your passwd for $router\n"
163				  catch {close}; catch {wait}; return 1
164				}
165	"Login failed"		{
166				  send_user "\nError: Check your passwd for $router\n"
167				  catch {close}; catch {wait}; return 1
168				}
169	-re "% (Bad passwords|Authentication failed)"	{
170				  send_user "\nError: Check your passwd for $router\n"
171				  catch {close}; catch {wait}; return 1
172				}
173	"Press any key to continue" {
174				  # send_user "Pressing the ANY key\n"
175				  send "\r"
176				  exp_continue
177				}
178	-re "Enter Selection: " {
179				  # Catalyst 1900s have some lame menu.  Enter
180				  # K to reach a command-line.
181				  send "K\r"
182				  exp_continue
183				}
184	-re "Last login:"	{
185				  exp_continue
186				}
187	-re "@\[^\r\n]+ $p_prompt"	{
188				  # ssh pwd prompt
189				  sleep 1
190				  send -- "$userpswd\r"
191				  exp_continue
192				}
193	-re "Enter passphrase.*: " {
194				  # sleep briefly to allow time for stty -echo
195				  sleep .3
196				  send -- "$passphrase\r"
197				  exp_continue
198				}
199	-re "$u_prompt"		{
200				  send -- "$user\r"
201				  set uprompt_seen 1
202				  exp_continue
203				}
204	-re "$p_prompt"		{
205				  sleep 1
206				  if {$uprompt_seen == 1} {
207					send -- "$userpswd\r"
208				  } else {
209					send -- "$passwd\r"
210				  }
211				  exp_continue
212				}
213	-re "$prompt"		{
214				  set prompt_match $expect_out(0,string);
215				  break;
216				}
217	"Login invalid"		{
218				  send_user "\nError: Invalid login: $router\n";
219				  catch {close}; catch {wait}; return 1
220				}
221	-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
222     }
223    }
224
225    set in_proc 0
226    return 0
227}
228
229# Enable
230proc do_enable { enauser enapasswd } {
231    global in_proc
232    global prompt u_prompt e_prompt enacmd
233    set in_proc 1
234
235    send "$enacmd\r"
236    expect {
237	-re "$u_prompt"	{ send -- "$enauser\r"; exp_continue}
238	-re "$e_prompt"	{ send -- "$enapasswd\r"; exp_continue}
239	"#"		{ set prompt "#" }
240	"(enable)"	{ set prompt "> \\(enable\\) " }
241	"% Invalid input" {
242			  send_user "\nError: Unrecognized command, check your enable command\n";
243			  return 1
244			}
245	-re "(denied|Sorry|Incorrect)"	{
246			  # % Access denied - from local auth and poss. others
247			  send_user "\nError: Check your Enable passwd\n";
248			  return 1
249			}
250	"% Error in authentication" {
251			  send_user "\nError: Check your Enable passwd\n"
252			  return 1
253			}
254	"% Bad passwords" {
255			  send_user "\nError: Check your Enable passwd\n"
256			  return 1
257			}
258    }
259    # We set the prompt variable (above) so script files don't need
260    # to know what it is.
261    set in_proc 0
262    return 0
263}
264
265# Run commands given on the command line.
266proc run_commands { prompt command } {
267    global do_interact in_proc
268    set in_proc 1
269
270    # match cisco config mode prompts too, such as router(config-if)#,
271    # but catalyst does not change in this fashion.
272    regsub -all {^(.*)([#>])$} $prompt {\1([^#>\r\n]+)?[#>](\\([^)\\r\\n]+\\))?} reprompt
273
274    # this is the only way i see to get rid of more prompts in o/p..grrrrr
275    log_user 0
276
277    # handle escaped ;s in commands, and ;; and ^;
278    regsub -all {([^\\]);} $command "\\1\u0002;" esccommand
279    regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command
280    regsub {^;} $command "\u0002;" esccommand
281    regsub -all {[\\];} $esccommand ";" command
282    regsub -all {\u0002;} $command "\u0002" esccommand
283    set sep "\u0002"
284    set commands [split $esccommand $sep]
285    set num_commands [llength $commands]
286    # the pager can not be turned off on the PIX, so we have to look
287    # for the "More" prompt.  the extreme is equally obnoxious in pre-12.3 XOS,
288    # with a global switch in the config.
289    for {set i 0} {$i < $num_commands} { incr i} {
290	send -- "[subst -nocommands [lindex $commands $i]]\r"
291	expect {
292	    -re "\b+"			{ exp_continue }
293	    -re "^\[^\n\r *]*$reprompt"	{ send_user -- "$expect_out(buffer)" }
294	    -re "^\[^\n\r]*$reprompt."	{
295					  send_user -- "$expect_out(buffer)"
296					  exp_continue
297					}
298	    -re "\[^\r\n]*\[\n\r]+"	{
299					  send_user -- "$expect_out(buffer)"
300					  exp_continue
301					}
302	}
303    }
304    log_user 1
305
306    if { $do_interact == 1 } {
307	interact
308	return 0
309    }
310
311    # quit works too
312    send -h "exit\r"
313    expect {
314	-re "^\[^\n\r *]*$reprompt"		{
315						  # if in enable mode, quit
316						  # returns to non-enabled mode
317						  send -h "exit\r"
318						  exp_continue;
319						}
320	-re "Are you sure .*to quit"		{
321						  catch {send "y\r"}
322						  exp_continue
323						}
324	-re "\[\n\r]+"				{ exp_continue }
325	timeout					{ catch {close}; catch {wait};
326						  return 0
327						}
328	eof					{ return 0 }
329    }
330    set in_proc 0
331}
332
333#
334# For each router... (this is main loop)
335#
336source_password_file $password_file
337set in_proc 0
338set exitval 0
339set prompt_match ""
340foreach router [lrange $argv $i end] {
341    set router [string tolower $router]
342    send_user -- "$router\n"
343
344    # device timeout
345    set timeout [find timeout $router]
346    if { [llength $timeout] == 0 } {
347	set timeout $timeoutdflt
348    }
349
350    # Default prompt.
351    set prompt [join [find prompt $router] ""]
352    if { [llength $prompt] == 0 } {
353	set prompt "(>|#)"
354    }
355
356    # look for autoenable option in .cloginrc & cmd-line
357    set ae [find autoenable $router]
358    if { "$ae" == "1" || $avautoenable } {
359	set autoenable 1
360    } else {
361	set autoenable 0
362    }
363    # look for enable options in .cloginrc & cmd-line
364    if { $avenable == 0 } {
365	set enable 0
366    } else {
367	set ne [find noenable $router]
368	if { "$ne" == "1" || "$autoenable" == "1" } {
369	    set enable 0
370	} else {
371	    set enable 1
372	}
373    }
374
375    # Figure out passwords
376    if { $do_passwd || $do_enapasswd } {
377	set pswd [find password $router]
378	if { [llength $pswd] == 0 } {
379	    send_user -- "\nError: no password for $router in $password_file.\n"
380	    continue
381	}
382	if { $enable && $do_enapasswd && $autoenable == 0 && [llength $pswd] < 2 } {
383	    send_user -- "\nError: no enable password for $router in $password_file.\n"
384	    continue
385	}
386	if { $do_passwd } {
387	    set passwd [join [lindex $pswd 0] ""]
388	} else {
389	    set passwd $userpasswd
390	}
391	if { $do_enapasswd } {
392	    set enapasswd [join [lindex $pswd 1] ""]
393	} else {
394	    set enapasswd $enapasswd
395	}
396    } else {
397	set passwd $userpasswd
398	set enapasswd $enapasswd
399    }
400
401    # Figure out username
402    if {[info exists username]} {
403      # command line username
404      set ruser $username
405    } else {
406      set ruser [join [find user $router] ""]
407      if { "$ruser" == "" } { set ruser $default_user }
408    }
409
410    # Figure out username's password (if different from the vty password)
411    if {[info exists userpasswd]} {
412      # command line username
413      set userpswd $userpasswd
414    } else {
415      set userpswd [join [find userpassword $router] ""]
416      if { "$userpswd" == "" } { set userpswd $passwd }
417    }
418
419    # Figure out enable username
420    if {[info exists enausername]} {
421      # command line enausername
422      set enauser $enausername
423    } else {
424      set enauser [join [find enauser $router] ""]
425      if { "$enauser" == "" } { set enauser $ruser }
426    }
427
428    # Figure out enable command
429    set enacmd [join [find enablecmd $router] ""]
430    if { "$enacmd" == "" } { set enacmd "enable" }
431
432    # Figure out prompts
433    set u_prompt [find userprompt $router]
434    if { "$u_prompt" == "" } {
435	set u_prompt "(\[Uu]sername|Login|login|user name|User):"
436    } else {
437	set u_prompt [join [lindex $u_prompt 0] ""]
438    }
439    set p_prompt [find passprompt $router]
440    if { "$p_prompt" == "" } {
441	set p_prompt "(\[Pp]assword|passwd|Enter password for \[^ :]+):"
442    } else {
443	set p_prompt [join [lindex $p_prompt 0] ""]
444    }
445    set e_prompt [find enableprompt $router]
446    if { "$e_prompt" == "" } {
447	set e_prompt "\[Pp]assword:"
448    } else {
449	set e_prompt [join [lindex $e_prompt 0] ""]
450    }
451
452    # Figure out identity file to use
453    set identfile [join [lindex [find identity $router] 0] ""]
454
455    # Figure out passphrase to use
456    if {[info exists avpassphrase]} {
457	set passphrase $avpassphrase
458    } else {
459	set passphrase [join [lindex [find passphrase $router] 0] ""]
460    }
461    if { ! [string length "$passphrase"]} {
462	set passphrase $passwd
463    }
464
465    # Figure out cypher type
466    if {[info exists cypher]} {
467	# command line cypher type
468	set cyphertype $cypher
469    } else {
470	set cyphertype [find cyphertype $router]
471    }
472
473    # Figure out connection method
474    set cmethod [find method $router]
475    if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
476
477    # Figure out the SSH executable name
478    set sshcmd [join [lindex [find sshcmd $router] 0] ""]
479    if { "$sshcmd" == "" } { set sshcmd {ssh} }
480
481    # Figure out the telnet executable name
482    set telnetcmd [join [lindex [find telnetcmd $router] 0] ""]
483    if { "$telnetcmd" == "" } { set telnetcmd "@TELNET_CMD@" }
484
485    # if [-mM], skip do not login
486    if { $do_cloginrcdbg > 0 } { continue; }
487
488    # Login to the router
489    if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype $identfile]} {
490	incr exitval
491	# if login failed or rsh was unsuccessful, move on to the next device
492	continue
493    }
494    # Figure out the prompt.
495    if { [regexp -- "#" $prompt_match junk] == 1 } {
496	set enable 0
497    }
498    if { $enable } {
499	if {[do_enable $enauser $enapasswd]} {
500	    if { $do_command || $do_script } {
501		incr exitval
502		catch {close}; catch {wait};
503		continue
504	    }
505	}
506    }
507    # we are logged in, now figure out the full prompt
508    send "\r"
509    regsub -all {^(\^*)(.*)} $prompt {\2} reprompt
510    expect {
511	-re "\[\r\n]+"		{ exp_continue; }
512	-re "^.+$reprompt"	{ set junk $expect_out(0,string);
513				  regsub -all "\[\]\[\(\)+]" $junk {\\&} prompt;
514				}
515    }
516    if { $do_command || $do_script } {
517	send "terminal length 0\r"
518	expect -re $prompt	{}
519	send "terminal width 0\r"
520	expect -re $prompt	{}
521    }
522    if { $do_command } {
523	if {[run_commands $prompt $command]} {
524	    incr exitval
525	    continue
526	}
527    } elseif { $do_script } {
528	source $sfile
529	catch {close};
530    } else {
531	label $router
532	log_user 1
533	interact
534    }
535
536    # End of for each router
537    catch {wait};
538    sleep 0.3
539}
540exit $exitval
541