1#! @EXPECT_PATH@ --
2##
3## @PACKAGE@ @VERSION@
4@copyright@
5#
6# nslogin - Netscaler login
7#
8# Hacks from Anshuman Kanwar.
9
10# The default is to automatically enable
11set avenable 0
12# Sometimes routers take awhile to answer (the default is 10 sec)
13set timeoutdflt 45
14# Some CLIs having problems if we write too fast (Extreme, PIX, Cat)
15set send_human {.2 .1 .4 .2 1}
16
17@login_top@
18
19# Log into the router.
20# returns: 0 on success, 1 on failure
21proc login { router user userpswd passwd enapasswd cmethod cyphertype } {
22    global spawn_id in_proc do_command do_script
23    global prompt u_prompt p_prompt e_prompt sshcmd
24    set in_proc 1
25    set uprompt_seen 0
26
27    # try each of the connection methods in $cmethod until one is successful
28    set progs [llength $cmethod]
29    foreach prog [lrange $cmethod 0 end] {
30	incr progs -1
31        if ![string compare $prog "ssh"] {
32	    regexp {ssh(:([^[:space:]]+))*} $prog methcmd suffix port
33	    set cmd $sshcmd
34	    if {"$port" != ""} {
35		set cmd "$cmd -p $port"
36	    }
37	    if {"$cyphertype" != ""} {
38		set cmd "$cmd -c $cyphertype"
39	    }
40	    set retval [ catch {eval spawn [split "$cmd -x -l $user $router" { }]} reason ]
41	    if { $retval } {
42		send_user "\nError: $cmd failed: $reason\n"
43		return 1
44	    }
45	} elseif ![string compare $prog "telnet"] {
46	    send_error "\nError: unsupported method: telnet\n"
47	    if { $progs == 0 } {
48		return 1
49	    }
50	    continue
51	} elseif ![string compare $prog "rsh"] {
52	    send_error "\nError: unsupported method: rsh\n"
53	    if { $progs == 0 } {
54		return 1
55	    }
56	    continue
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 "(Connection refused|Secure connection \[^\n\r]+ refused|Connection closed by)" {
95	    catch {close}; catch {wait};
96	    if !$progs {
97		send_user "\nError: Connection Refused ($prog)\n"; return 1
98	    }
99	}
100	eof { send_user "\nError: Couldn't login\n"; wait; return 1 }
101	-nocase "unknown host\r" {
102	    catch {close}; catch {wait};
103	    send_user "\nError: Unknown host\n"; wait; return 1
104	}
105	"Host is unreachable" {
106	    catch {close}; catch {wait};
107	    send_user "\nError: Host Unreachable!\n"; wait; return 1
108	}
109	"No address associated with name" {
110	    catch {close}; catch {wait};
111	    send_user "\nError: Unknown host\n"; wait; return 1
112	}
113	-re "(Host key not found |The authenticity of host .* be established)" {
114	    expect {
115		-re "\\(yes\/no\[^\\)]*\\)\\?" {
116					  send "yes\r";
117					  send_user "\nHost $router added to the list of known hosts.\n"
118					 }
119		-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
120	    }
121	    exp_continue
122	}
123	-re "HOST IDENTIFICATION HAS CHANGED" {
124	    send_user "\nError: The host key for $router has changed.  Update the SSH known_hosts file accordingly.\n"
125	    expect {
126		-re "\\(yes\/no\\)\\?"	{ send "no\r" }
127		-re " strict checking\.\[\r\n]+" { }
128		-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
129	    }
130	    catch {close}; catch {wait};
131	    return 1
132	}
133	-re "Offending key for " {
134	    send_user "\nError: host key mismatch for $router.  Update the SSH known_hosts file accordingly.\n"
135	    expect {
136		-re "\\(yes\/no\\)\\?"	{ send "no\r" }
137		-re "\[^\r\n]*\[\r\n]+"	{ exp_continue; }
138	    }
139	    catch {close}; catch {wait};
140	    return 1
141	}
142	-re "(denied|Sorry)"	{
143				  send_user "\nError: Check your passwd for $router\n"
144				  catch {close}; catch {wait}; return 1
145				}
146	"Login failed"		{
147				  send_user "\nError: Check your passwd for $router\n"
148				  return 1
149				}
150	-re "% (Bad passwords|Authentication failed)"	{
151				  send_user "\nError: Check your passwd for $router\n"
152				  return 1
153				}
154	# newer netscaler code (NS8.0: Build 47.8) the password prompt is
155	# "Password:" not "user@hosts's password:"
156	-re "(@\[^\r\n]+ )?$p_prompt"	{
157					  # ssh pwd prompt
158					  sleep 1
159					  send -- "$userpswd\r"
160					  exp_continue
161					}
162
163	"$prompt"		{ break; }
164	"Login invalid"		{
165				  send_user "\nError: Invalid login\n";
166				  catch {close}; catch {wait}; return 1
167				}
168     }
169    }
170
171    set in_proc 0
172    return 0
173}
174
175
176# Run commands given on the command line.
177
178proc run_commands { prompt command } {
179    global do_interact in_proc
180    set in_proc 1
181
182    # escape any parens in the prompt, such as "(enable)"
183    regsub -all "\[)(]" $prompt {\\&} reprompt
184    # this is the only way i see to get rid of more prompts in o/p..grrrrr
185    log_user 0
186
187    # handle escaped ;s in commands, and ;; and ^;
188    regsub -all {([^\\]);} $command "\\1\u0002;" esccommand
189    regsub -all {([^\\]);;} $esccommand "\\1;\u0002;" command
190    regsub {^;} $command "\u0002;" esccommand
191    regsub -all {[\\];} $esccommand ";" command
192    regsub -all {\u0002;} $command "\u0002" esccommand
193    set sep "\u0002"
194    set commands [split $esccommand $sep]
195    set num_commands [llength $commands]
196    # the pager can not be turned off on the PIX, so we have to look
197    # for the "More" prompt.  the extreme is equally obnoxious, with a
198    # global switch in the config.
199    for {set i 0} {$i < $num_commands} { incr i} {
200	send -- "[subst -nocommands [lindex $commands $i]]\r"
201	expect {
202	    -re "\b+"				{ exp_continue }
203	    -re "^\[^\n\r *]*$reprompt"		{ send_user -- "$expect_out(buffer)"
204						}
205	    -re "^\[^\n\r]*$reprompt."		{ send_user -- "$expect_out(buffer)"
206						  exp_continue
207						}
208	    -re "\[\n\r]+"			{ send_user -- "$expect_out(buffer)"
209						  exp_continue
210						}
211	    -re "\[^\r\n]*Press <SPACE> to cont\[^\r\n]*" {
212						  send " "
213						  # bloody ^[[2K after " "
214						  expect {
215						    -re "^\[^\r\n]*\r" {}
216						  }
217						  exp_continue
218						}
219	    -re "^ --More--\[^\n\r]*"		{
220						  send " "
221						  exp_continue
222						}
223	    -re "^<-+ More -+>\[^\n\r]*"	{
224						  send_user -- "$expect_out(buffer)"
225						  send " "
226						  exp_continue
227						}
228	}
229    }
230    log_user 1
231
232    if { $do_interact == 1 } {
233	interact
234	return 0
235    }
236
237    send "quit\r"
238    expect {
239        "Do you wish to save your configuration changes" {
240                                                  send "n\r"
241                                                  exp_continue
242                                                }
243        "\n"					{ exp_continue }
244        timeout					{ catch {close}; catch {wait};
245						  return 0
246						}
247        eof					{ return 0 }
248    }
249    set in_proc 0
250}
251
252
253
254#
255# For each router... (this is main loop)
256#
257source_password_file $password_file
258set in_proc 0
259set exitval 0
260foreach router [lrange $argv $i end] {
261    set router [string tolower $router]
262    send_user "$router\n"
263
264    # device timeout
265    set timeout [find timeout $router]
266    if { [llength $timeout] == 0 } {
267	set timeout $timeoutdflt
268    }
269
270    # Figure out prompt.
271    set prompt ">"
272
273    # look for noenable option in .cloginrc
274    if { [find noenable $router] == "1" } {
275	set enable 0
276    } else {
277	set enable $avenable
278    }
279
280    # Figure out passwords
281    if { $do_passwd } {
282	set pswd [find password $router]
283	if { [llength $pswd] == 0 } {
284	    send_user -- "\nError: no password for $router in $password_file.\n"
285	    continue
286	}
287	if { $enable && $autoenable == 0 && [llength $pswd] < 2 } {
288	    send_user -- "\nError: no enable password for $router in $password_file.\n"
289	    continue
290	}
291	if { $do_passwd } {
292	    set passwd [join [lindex $pswd 0] ""]
293	} else {
294	    set passwd $userpasswd
295	}
296	if { $do_enapasswd } {
297	    set enapasswd [join [lindex $pswd 1] ""]
298	} else {
299	    set enapasswd $enapasswd
300	}
301    } else {
302	set passwd $userpasswd
303	set enapasswd $enapasswd
304    }
305
306    # Figure out username
307    if {[info exists username]} {
308      # command line username
309      set ruser $username
310    } else {
311      set ruser [join [find user $router] ""]
312      if { "$ruser" == "" } { set ruser $default_user }
313    }
314
315    # Figure out username's password (if different from the vty password)
316    if {[info exists userpasswd]} {
317      # command line username
318      set userpswd $userpasswd
319    } else {
320      set userpswd [join [find userpassword $router] ""]
321      if { "$userpswd" == "" } { set userpswd $passwd }
322    }
323
324    # Figure out enable username
325    if {[info exists enausername]} {
326      # command line enausername
327      set enauser $enausername
328    } else {
329      set enauser [join [find enauser $router] ""]
330      if { "$enauser" == "" } { set enauser $ruser }
331    }
332
333    # Figure out prompts
334    set u_prompt [find userprompt $router]
335    if { "$u_prompt" == "" } {
336	set u_prompt "(Username|Login|login|user name):"
337    } else {
338	set u_prompt [join [lindex $u_prompt 0] ""]
339    }
340    set p_prompt [find passprompt $router]
341    if { "$p_prompt" == "" } {
342	set p_prompt "(\[Pp]assword|passwd):"
343    } else {
344	set p_prompt [join [lindex $p_prompt 0] ""]
345    }
346    set e_prompt [find enableprompt $router]
347    if { "$e_prompt" == "" } {
348	set e_prompt "\[Pp]assword:"
349    } else {
350	set e_prompt [join [lindex $e_prompt 0] ""]
351    }
352
353    # Figure out cypher type
354    if {[info exists cypher]} {
355      # command line cypher type
356      set cyphertype $cypher
357    } else {
358      set cyphertype [find cyphertype $router]
359    }
360
361    # Figure out connection method
362    set cmethod [find method $router]
363    if { "$cmethod" == "" } { set cmethod {{telnet} {ssh}} }
364
365    # Figure out the SSH executable name
366    set sshcmd [join [lindex [find sshcmd $router] 0] ""]
367    if { "$sshcmd" == "" } { set sshcmd {ssh} }
368
369    # if [-mM], skip do not login
370    if { $do_cloginrcdbg > 0 } { continue; }
371
372    # Login to the router
373    if {[login $router $ruser $userpswd $passwd $enapasswd $cmethod $cyphertype]} {
374	incr exitval
375	continue
376    }
377
378    # we are logged in, now figure out the full prompt
379    send "\r"
380    expect {
381	-re "\[\r\n]+"		{ exp_continue; }
382	-re "^$prompt"		{ set junk $expect_out(0,string);
383				  regsub -all "\[\]\[]" $junk {\\&} prompt; }
384    }
385
386    if { $do_command } {
387	if {[run_commands $prompt $command]} {
388	    incr exitval
389	    continue
390	}
391    } elseif { $do_script } {
392	# If the prompt is (enable), then we are on a switch and the
393	# command is "set length 0"; otherwise its "term length 0".
394	if [ regexp -- ".*> .*enable" "$prompt" ] {
395	    send "set length 0\r"
396	    send "set logging session disable\r"
397	} else {
398	    send "term length 0\r"
399	}
400	expect -re $prompt	{}
401	source $sfile
402	catch {close};
403    } else {
404	label $router
405	log_user 1
406	interact
407    }
408
409    # End of for each router
410    catch {wait};
411    sleep 0.3
412}
413exit $exitval
414