Mercurial > 510Connectbot
annotate src/com/five_ten_sg/connectbot/transport/SSH.java @ 277:e0da43026046 ganymed
start conversion from trilead to ganymed
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 18 Jul 2014 11:59:52 -0700 |
parents | 91a31873c42a |
children | 8c55d7714d03 |
rev | line source |
---|---|
0 | 1 /* |
2 * ConnectBot: simple, powerful, open-source SSH client for Android | |
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey | |
4 * | |
5 * Licensed under the Apache License, Version 2.0 (the "License"); | |
6 * you may not use this file except in compliance with the License. | |
7 * You may obtain a copy of the License at | |
8 * | |
9 * http://www.apache.org/licenses/LICENSE-2.0 | |
10 * | |
11 * Unless required by applicable law or agreed to in writing, software | |
12 * distributed under the License is distributed on an "AS IS" BASIS, | |
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 * See the License for the specific language governing permissions and | |
15 * limitations under the License. | |
16 */ | |
17 | |
18 package com.five_ten_sg.connectbot.transport; | |
19 | |
20 import java.io.File; | |
21 import java.io.IOException; | |
22 import java.io.InputStream; | |
23 import java.io.OutputStream; | |
24 import java.net.InetAddress; | |
25 import java.net.InetSocketAddress; | |
26 import java.net.URL; | |
27 import java.net.MalformedURLException; | |
28 import java.security.KeyPair; | |
29 import java.security.NoSuchAlgorithmException; | |
30 import java.security.PrivateKey; | |
31 import java.security.PublicKey; | |
32 import java.security.interfaces.DSAPrivateKey; | |
33 import java.security.interfaces.DSAPublicKey; | |
34 import java.security.interfaces.RSAPrivateKey; | |
35 import java.security.interfaces.RSAPublicKey; | |
36 import java.security.spec.InvalidKeySpecException; | |
37 import java.util.Arrays; | |
38 import java.util.HashMap; | |
39 import java.util.LinkedList; | |
40 import java.util.List; | |
41 import java.util.Locale; | |
42 import java.util.Map; | |
43 import java.util.Map.Entry; | |
44 import java.util.regex.Matcher; | |
45 import java.util.regex.Pattern; | |
46 | |
47 import com.five_ten_sg.connectbot.R; | |
48 import com.five_ten_sg.connectbot.bean.HostBean; | |
49 import com.five_ten_sg.connectbot.bean.PortForwardBean; | |
50 import com.five_ten_sg.connectbot.bean.PubkeyBean; | |
51 import com.five_ten_sg.connectbot.service.TerminalBridge; | |
52 import com.five_ten_sg.connectbot.service.TerminalManager; | |
53 import com.five_ten_sg.connectbot.service.TerminalManager.KeyHolder; | |
54 import com.five_ten_sg.connectbot.util.HostDatabase; | |
55 import com.five_ten_sg.connectbot.util.PubkeyDatabase; | |
56 import com.five_ten_sg.connectbot.util.PubkeyUtils; | |
57 import android.content.Context; | |
58 import android.net.Uri; | |
59 import android.os.Environment; | |
60 import android.util.Log; | |
61 | |
273
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
62 import ch.ethz.ssh2.AuthAgentCallback; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
63 import ch.ethz.ssh2.ChannelCondition; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
64 import ch.ethz.ssh2.Connection; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
65 import ch.ethz.ssh2.ConnectionInfo; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
66 import ch.ethz.ssh2.ConnectionMonitor; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
67 import ch.ethz.ssh2.DynamicPortForwarder; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
68 import ch.ethz.ssh2.InteractiveCallback; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
69 import ch.ethz.ssh2.KnownHosts; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
70 import ch.ethz.ssh2.LocalPortForwarder; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
71 import ch.ethz.ssh2.SCPClient; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
72 import ch.ethz.ssh2.ServerHostKeyVerifier; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
73 import ch.ethz.ssh2.Session; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
74 import ch.ethz.ssh2.HTTPProxyData; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
75 import ch.ethz.ssh2.HTTPProxyException; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
76 import ch.ethz.ssh2.crypto.PEMDecoder; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
77 import ch.ethz.ssh2.signature.DSASHA1Verify; |
91a31873c42a
start conversion from trilead to ganymed
Carl Byington <carl@five-ten-sg.com>
parents:
112
diff
changeset
|
78 import ch.ethz.ssh2.signature.RSASHA1Verify; |
0 | 79 |
80 /** | |
81 * @author Kenny Root | |
82 * | |
83 */ | |
84 public class SSH extends AbsTransport implements ConnectionMonitor, InteractiveCallback, AuthAgentCallback { | |
85 private static final String PROTOCOL = "ssh"; | |
86 private static final String TAG = "ConnectBot.SSH"; | |
87 private static final int DEFAULT_PORT = 22; | |
88 | |
89 private static final String AUTH_PUBLICKEY = "publickey", | |
90 AUTH_PASSWORD = "password", | |
91 AUTH_KEYBOARDINTERACTIVE = "keyboard-interactive"; | |
92 | |
93 private final static int AUTH_TRIES = 20; | |
94 | |
95 static final Pattern hostmask; | |
96 static { | |
97 hostmask = Pattern.compile("^(.+)@([0-9a-z.-]+)(:(\\d+))?$", Pattern.CASE_INSENSITIVE); | |
98 } | |
99 | |
100 private boolean compression = false; | |
101 private String httpproxy = null; | |
102 private volatile boolean authenticated = false; | |
103 private volatile boolean connected = false; | |
104 private volatile boolean sessionOpen = false; | |
105 | |
106 private boolean pubkeysExhausted = false; | |
107 private boolean interactiveCanContinue = true; | |
108 | |
109 private Connection connection; | |
110 private Session session; | |
111 private ConnectionInfo connectionInfo; | |
112 | |
113 private OutputStream stdin; | |
114 private InputStream stdout; | |
115 private InputStream stderr; | |
116 | |
117 private static final int conditions = ChannelCondition.STDOUT_DATA | |
118 | ChannelCondition.STDERR_DATA | |
119 | ChannelCondition.CLOSED | |
120 | ChannelCondition.EOF; | |
121 | |
122 private List<PortForwardBean> portForwards = new LinkedList<PortForwardBean>(); | |
123 | |
124 private int columns; | |
125 private int rows; | |
126 | |
127 private int width; | |
128 private int height; | |
129 | |
130 private String useAuthAgent = HostDatabase.AUTHAGENT_NO; | |
131 private String agentLockPassphrase; | |
132 | |
133 public class HostKeyVerifier implements ServerHostKeyVerifier { | |
134 public boolean verifyServerHostKey(String hostname, int port, | |
135 String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException { | |
136 // read in all known hosts from hostdb | |
137 KnownHosts hosts = manager.hostdb.getKnownHosts(); | |
138 Boolean result; | |
139 String matchName = String.format(Locale.US, "%s:%d", hostname, port); | |
140 String fingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); | |
141 String algorithmName; | |
142 | |
143 if ("ssh-rsa".equals(serverHostKeyAlgorithm)) | |
144 algorithmName = "RSA"; | |
145 else if ("ssh-dss".equals(serverHostKeyAlgorithm)) | |
146 algorithmName = "DSA"; | |
147 else if (serverHostKeyAlgorithm.startsWith("ecdsa-")) | |
148 algorithmName = "EC"; | |
149 else | |
150 algorithmName = serverHostKeyAlgorithm; | |
151 | |
152 switch (hosts.verifyHostkey(matchName, serverHostKeyAlgorithm, serverHostKey)) { | |
153 case KnownHosts.HOSTKEY_IS_OK: | |
154 bridge.outputLine(manager.res.getString(R.string.terminal_sucess, algorithmName, fingerprint)); | |
155 return true; | |
156 | |
157 case KnownHosts.HOSTKEY_IS_NEW: | |
158 // prompt user | |
159 bridge.outputLine(manager.res.getString(R.string.host_authenticity_warning, hostname)); | |
160 bridge.outputLine(manager.res.getString(R.string.host_fingerprint, algorithmName, fingerprint)); | |
161 result = bridge.promptHelper.requestBooleanPrompt(null, manager.res.getString(R.string.prompt_continue_connecting)); | |
162 | |
163 if (result == null) return false; | |
164 | |
165 if (result.booleanValue()) { | |
166 // save this key in known database | |
167 manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); | |
168 } | |
169 | |
170 return result.booleanValue(); | |
171 | |
172 case KnownHosts.HOSTKEY_HAS_CHANGED: | |
173 String header = String.format("@ %s @", | |
174 manager.res.getString(R.string.host_verification_failure_warning_header)); | |
175 char[] atsigns = new char[header.length()]; | |
176 Arrays.fill(atsigns, '@'); | |
177 String border = new String(atsigns); | |
178 bridge.outputLine(border); | |
179 bridge.outputLine(manager.res.getString(R.string.host_verification_failure_warning)); | |
180 bridge.outputLine(border); | |
181 bridge.outputLine(String.format(manager.res.getString(R.string.host_fingerprint), | |
182 algorithmName, fingerprint)); | |
183 // Users have no way to delete keys, so we'll prompt them for now. | |
184 result = bridge.promptHelper.requestBooleanPrompt(null, manager.res.getString(R.string.prompt_continue_connecting)); | |
185 | |
186 if (result == null) return false; | |
187 | |
188 if (result.booleanValue()) { | |
189 // save this key in known database | |
190 manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); | |
191 } | |
192 | |
193 return result.booleanValue(); | |
194 | |
195 default: | |
196 return false; | |
197 } | |
198 } | |
199 | |
200 } | |
201 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
202 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
203 public SSH() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
204 super(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
205 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
206 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
207 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
208 /** |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
209 * @return protocol part of the URI |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
210 */ |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
211 public static String getProtocolName() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
212 return PROTOCOL; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
213 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
214 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
215 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
216 public Uri getUri(String input) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
217 Matcher matcher = hostmask.matcher(input); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
218 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
219 if (!matcher.matches()) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
220 return null; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
221 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
222 StringBuilder sb = new StringBuilder(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
223 sb.append(PROTOCOL) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
224 .append("://") |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
225 .append(Uri.encode(matcher.group(1))) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
226 .append('@') |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
227 .append(matcher.group(2)); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
228 String portString = matcher.group(4); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
229 int port = DEFAULT_PORT; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
230 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
231 if (portString != null) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
232 try { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
233 port = Integer.parseInt(portString); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
234 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
235 if (port < 1 || port > 65535) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
236 port = DEFAULT_PORT; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
237 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
238 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
239 catch (NumberFormatException nfe) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
240 // Keep the default port |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
241 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
242 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
243 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
244 if (port != DEFAULT_PORT) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
245 sb.append(':') |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
246 .append(port); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
247 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
248 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
249 sb.append("/#") |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
250 .append(Uri.encode(input)); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
251 Uri uri = Uri.parse(sb.toString()); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
252 return uri; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
253 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
254 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
255 |
0 | 256 private void authenticate() { |
257 try { | |
258 if (connection.authenticateWithNone(host.getUsername())) { | |
259 finishConnection(); | |
260 return; | |
261 } | |
262 } | |
263 catch (Exception e) { | |
264 Log.d(TAG, "Host does not support 'none' authentication."); | |
265 } | |
266 | |
267 bridge.outputLine(manager.res.getString(R.string.terminal_auth)); | |
268 | |
269 try { | |
270 long pubkeyId = host.getPubkeyId(); | |
271 | |
272 if (!pubkeysExhausted && | |
273 pubkeyId != HostDatabase.PUBKEYID_NEVER && | |
274 connection.isAuthMethodAvailable(host.getUsername(), AUTH_PUBLICKEY)) { | |
275 // if explicit pubkey defined for this host, then prompt for password as needed | |
276 // otherwise just try all in-memory keys held in terminalmanager | |
277 if (pubkeyId == HostDatabase.PUBKEYID_ANY) { | |
278 // try each of the in-memory keys | |
279 bridge.outputLine(manager.res | |
280 .getString(R.string.terminal_auth_pubkey_any)); | |
281 | |
282 for (Entry<String, KeyHolder> entry : manager.loadedKeypairs.entrySet()) { | |
283 if (entry.getValue().bean.isConfirmUse() | |
284 && !promptForPubkeyUse(entry.getKey())) | |
285 continue; | |
286 | |
287 if (this.tryPublicKey(host.getUsername(), entry.getKey(), | |
288 entry.getValue().pair)) { | |
289 finishConnection(); | |
290 break; | |
291 } | |
292 } | |
293 } | |
294 else { | |
295 bridge.outputLine(manager.res.getString(R.string.terminal_auth_pubkey_specific)); | |
296 // use a specific key for this host, as requested | |
297 PubkeyBean pubkey = manager.pubkeydb.findPubkeyById(pubkeyId); | |
298 | |
299 if (pubkey == null) | |
300 bridge.outputLine(manager.res.getString(R.string.terminal_auth_pubkey_invalid)); | |
301 else if (tryPublicKey(pubkey)) | |
302 finishConnection(); | |
303 } | |
304 | |
305 pubkeysExhausted = true; | |
306 } | |
307 else if (interactiveCanContinue && | |
308 connection.isAuthMethodAvailable(host.getUsername(), AUTH_KEYBOARDINTERACTIVE)) { | |
309 // this auth method will talk with us using InteractiveCallback interface | |
310 // it blocks until authentication finishes | |
311 bridge.outputLine(manager.res.getString(R.string.terminal_auth_ki)); | |
312 interactiveCanContinue = false; | |
313 | |
314 if (connection.authenticateWithKeyboardInteractive(host.getUsername(), this)) { | |
315 finishConnection(); | |
316 } | |
317 else { | |
318 bridge.outputLine(manager.res.getString(R.string.terminal_auth_ki_fail)); | |
319 } | |
320 } | |
321 else if (connection.isAuthMethodAvailable(host.getUsername(), AUTH_PASSWORD)) { | |
322 bridge.outputLine(manager.res.getString(R.string.terminal_auth_pass)); | |
323 String password = bridge.getPromptHelper().requestPasswordPrompt(null, | |
324 manager.res.getString(R.string.prompt_password)); | |
325 | |
326 if (password != null | |
327 && connection.authenticateWithPassword(host.getUsername(), password)) { | |
328 finishConnection(); | |
329 } | |
330 else { | |
331 bridge.outputLine(manager.res.getString(R.string.terminal_auth_pass_fail)); | |
332 } | |
333 } | |
334 else { | |
335 bridge.outputLine(manager.res.getString(R.string.terminal_auth_fail)); | |
336 } | |
337 } | |
338 catch (IllegalStateException e) { | |
339 Log.e(TAG, "Connection went away while we were trying to authenticate", e); | |
340 return; | |
341 } | |
342 catch (Exception e) { | |
343 Log.e(TAG, "Problem during handleAuthentication()", e); | |
344 } | |
345 } | |
346 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
347 |
0 | 348 /** |
349 * Attempt connection with database row pointed to by cursor. | |
350 * @param cursor | |
351 * @return true for successful authentication | |
352 * @throws NoSuchAlgorithmException | |
353 * @throws InvalidKeySpecException | |
354 * @throws IOException | |
355 */ | |
356 private boolean tryPublicKey(PubkeyBean pubkey) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { | |
357 KeyPair pair = null; | |
358 | |
359 if (manager.isKeyLoaded(pubkey.getNickname())) { | |
360 // load this key from memory if its already there | |
361 Log.d(TAG, String.format("Found unlocked key '%s' already in-memory", pubkey.getNickname())); | |
362 | |
363 if (pubkey.isConfirmUse()) { | |
364 if (!promptForPubkeyUse(pubkey.getNickname())) | |
365 return false; | |
366 } | |
367 | |
368 pair = manager.getKey(pubkey.getNickname()); | |
369 } | |
370 else { | |
371 // otherwise load key from database and prompt for password as needed | |
372 String password = null; | |
373 | |
374 if (pubkey.isEncrypted()) { | |
375 password = bridge.getPromptHelper().requestPasswordPrompt(null, | |
376 manager.res.getString(R.string.prompt_pubkey_password, pubkey.getNickname())); | |
377 | |
378 // Something must have interrupted the prompt. | |
379 if (password == null) | |
380 return false; | |
381 } | |
382 | |
383 if (PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType())) { | |
384 // load specific key using pem format | |
385 pair = PEMDecoder.decode(new String(pubkey.getPrivateKey()).toCharArray(), password); | |
386 } | |
387 else { | |
388 // load using internal generated format | |
389 PrivateKey privKey; | |
390 | |
391 try { | |
392 privKey = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), | |
393 pubkey.getType(), password); | |
394 } | |
395 catch (Exception e) { | |
396 String message = String.format("Bad password for key '%s'. Authentication failed.", pubkey.getNickname()); | |
397 Log.e(TAG, message, e); | |
398 bridge.outputLine(message); | |
399 return false; | |
400 } | |
401 | |
402 PublicKey pubKey = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType()); | |
403 // convert key to trilead format | |
404 pair = new KeyPair(pubKey, privKey); | |
405 Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey)); | |
406 } | |
407 | |
408 Log.d(TAG, String.format("Unlocked key '%s'", pubkey.getNickname())); | |
409 // save this key in memory | |
410 manager.addKey(pubkey, pair); | |
411 } | |
412 | |
413 return tryPublicKey(host.getUsername(), pubkey.getNickname(), pair); | |
414 } | |
415 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
416 |
0 | 417 private boolean tryPublicKey(String username, String keyNickname, KeyPair pair) throws IOException { |
418 //bridge.outputLine(String.format("Attempting 'publickey' with key '%s' [%s]...", keyNickname, trileadKey.toString())); | |
419 boolean success = connection.authenticateWithPublicKey(username, pair); | |
420 | |
421 if (!success) | |
422 bridge.outputLine(manager.res.getString(R.string.terminal_auth_pubkey_fail, keyNickname)); | |
423 | |
424 return success; | |
425 } | |
426 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
427 |
0 | 428 /** |
429 * Internal method to request actual PTY terminal once we've finished | |
430 * authentication. If called before authenticated, it will just fail. | |
431 */ | |
432 private void finishConnection() { | |
433 authenticated = true; | |
434 | |
435 for (PortForwardBean portForward : portForwards) { | |
436 try { | |
437 enablePortForward(portForward); | |
438 bridge.outputLine(manager.res.getString(R.string.terminal_enable_portfoward, portForward.getDescription())); | |
439 } | |
440 catch (Exception e) { | |
441 Log.e(TAG, "Error setting up port forward during connect", e); | |
442 } | |
443 } | |
444 | |
445 if (!host.getWantSession()) { | |
446 bridge.outputLine(manager.res.getString(R.string.terminal_no_session)); | |
447 bridge.onConnected(); | |
448 return; | |
449 } | |
450 | |
451 try { | |
452 session = connection.openSession(); | |
453 | |
454 if (!useAuthAgent.equals(HostDatabase.AUTHAGENT_NO)) | |
455 session.requestAuthAgentForwarding(this); | |
456 | |
457 if (host.getWantX11Forward()) { | |
458 try { | |
459 session.requestX11Forwarding(host.getX11Host(), host.getX11Port(), null, false); | |
460 } | |
461 catch (IOException e2) { | |
462 Log.e(TAG, "Problem while trying to setup X11 forwarding in finishConnection()", e2); | |
463 } | |
464 } | |
465 | |
466 session.requestPTY(getEmulation(), columns, rows, width, height, null); | |
467 session.startShell(); | |
468 stdin = session.getStdin(); | |
469 stdout = session.getStdout(); | |
470 stderr = session.getStderr(); | |
471 sessionOpen = true; | |
472 bridge.onConnected(); | |
473 } | |
474 catch (IOException e1) { | |
475 Log.e(TAG, "Problem while trying to create PTY in finishConnection()", e1); | |
476 } | |
477 } | |
478 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
479 |
0 | 480 @Override |
481 public void connect() { | |
482 connection = new Connection(host.getHostname(), host.getPort()); | |
483 connection.addConnectionMonitor(this); | |
484 | |
485 try { | |
486 connection.setCompression(compression); | |
487 } | |
488 catch (IOException e) { | |
489 Log.e(TAG, "Could not enable compression!", e); | |
490 } | |
491 | |
492 if (httpproxy != null && httpproxy.length() > 0) { | |
493 Log.d(TAG, "Want HTTP Proxy: " + httpproxy, null); | |
494 | |
495 try { | |
496 URL u; | |
497 | |
498 if (httpproxy.startsWith("http://")) { | |
499 u = new URL(httpproxy); | |
500 } | |
501 else { | |
502 u = new URL("http://" + httpproxy); | |
503 } | |
504 | |
505 connection.setProxyData(new HTTPProxyData( | |
506 u.getHost(), | |
507 u.getPort(), | |
508 u.getUserInfo(), | |
509 u.getAuthority())); | |
510 bridge.outputLine("Connecting via proxy: " + httpproxy); | |
511 } | |
512 catch (MalformedURLException e) { | |
513 Log.e(TAG, "Could not parse proxy " + httpproxy, e); | |
514 // Display the reason in the text. | |
515 bridge.outputLine("Bad proxy URL: " + httpproxy); | |
516 onDisconnect(); | |
517 return; | |
518 } | |
519 } | |
520 | |
521 try { | |
522 /* Uncomment when debugging SSH protocol: | |
523 DebugLogger logger = new DebugLogger() { | |
524 | |
525 public void log(int level, String className, String message) { | |
526 Log.d("SSH", message); | |
527 } | |
528 | |
529 }; | |
530 Logger.enabled = true; | |
531 Logger.logger = logger; | |
532 */ | |
533 connectionInfo = connection.connect(new HostKeyVerifier()); | |
534 connected = true; | |
535 | |
536 if (connectionInfo.clientToServerCryptoAlgorithm | |
537 .equals(connectionInfo.serverToClientCryptoAlgorithm) | |
538 && connectionInfo.clientToServerMACAlgorithm | |
539 .equals(connectionInfo.serverToClientMACAlgorithm)) { | |
540 bridge.outputLine(manager.res.getString(R.string.terminal_using_algorithm, | |
541 connectionInfo.clientToServerCryptoAlgorithm, | |
542 connectionInfo.clientToServerMACAlgorithm)); | |
543 } | |
544 else { | |
545 bridge.outputLine(manager.res.getString( | |
546 R.string.terminal_using_c2s_algorithm, | |
547 connectionInfo.clientToServerCryptoAlgorithm, | |
548 connectionInfo.clientToServerMACAlgorithm)); | |
549 bridge.outputLine(manager.res.getString( | |
550 R.string.terminal_using_s2c_algorithm, | |
551 connectionInfo.serverToClientCryptoAlgorithm, | |
552 connectionInfo.serverToClientMACAlgorithm)); | |
553 } | |
554 } | |
555 catch (HTTPProxyException e) { | |
556 Log.e(TAG, "Failed to connect to HTTP Proxy", e); | |
557 // Display the reason in the text. | |
558 bridge.outputLine("Failed to connect to HTTP Proxy."); | |
559 onDisconnect(); | |
560 return; | |
561 } | |
562 catch (IOException e) { | |
563 Log.e(TAG, "Problem in SSH connection thread during authentication", e); | |
564 // Display the reason in the text. | |
565 bridge.outputLine(e.getCause().getMessage()); | |
566 onDisconnect(); | |
567 return; | |
568 } | |
569 | |
570 try { | |
571 // enter a loop to keep trying until authentication | |
572 int tries = 0; | |
573 | |
574 while (connected && !connection.isAuthenticationComplete() && tries++ < AUTH_TRIES) { | |
575 authenticate(); | |
576 // sleep to make sure we dont kill system | |
577 Thread.sleep(1000); | |
578 } | |
579 } | |
580 catch (Exception e) { | |
581 Log.e(TAG, "Problem in SSH connection thread during authentication", e); | |
582 } | |
583 } | |
584 | |
585 | |
586 @Override | |
587 public boolean willBlock() { | |
588 if (stdout == null) return true; | |
112
77ac18bc1b2f
cleanup java formatting
Carl Byington <carl@five-ten-sg.com>
parents:
31
diff
changeset
|
589 |
0 | 590 try { |
591 return stdout.available() == 0; | |
112
77ac18bc1b2f
cleanup java formatting
Carl Byington <carl@five-ten-sg.com>
parents:
31
diff
changeset
|
592 } |
77ac18bc1b2f
cleanup java formatting
Carl Byington <carl@five-ten-sg.com>
parents:
31
diff
changeset
|
593 catch (Exception e) { |
0 | 594 return true; |
595 } | |
596 } | |
597 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
598 |
0 | 599 @Override |
600 public int read(byte[] buffer, int start, int len) throws IOException { | |
601 int bytesRead = 0; | |
602 | |
603 if (session == null) | |
604 return 0; | |
605 | |
606 int newConditions = session.waitForCondition(conditions, 0); | |
607 | |
608 if ((newConditions & ChannelCondition.STDOUT_DATA) != 0) { | |
609 bytesRead = stdout.read(buffer, start, len); | |
610 } | |
611 | |
612 if ((newConditions & ChannelCondition.STDERR_DATA) != 0) { | |
613 byte discard[] = new byte[256]; | |
614 | |
615 while (stderr.available() > 0) { | |
616 stderr.read(discard); | |
617 } | |
618 } | |
619 | |
620 if ((newConditions & ChannelCondition.EOF) != 0) { | |
621 onDisconnect(); | |
622 throw new IOException("Remote end closed connection"); | |
623 } | |
624 | |
625 return bytesRead; | |
626 } | |
627 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
628 |
0 | 629 @Override |
630 public void write(byte[] buffer) throws IOException { | |
631 if (stdin != null) | |
632 stdin.write(buffer); | |
633 } | |
634 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
635 |
0 | 636 @Override |
637 public void write(int c) throws IOException { | |
638 if (stdin != null) | |
639 stdin.write(c); | |
640 } | |
641 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
642 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
643 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
644 public void flush() throws IOException { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
645 if (stdin != null) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
646 stdin.flush(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
647 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
648 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
649 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
650 public void connectionLost(Throwable reason) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
651 onDisconnect(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
652 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
653 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
654 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
655 private void onDisconnect() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
656 close(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
657 bridge.dispatchDisconnect(false); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
658 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
659 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
660 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
661 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
662 public void close() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
663 connected = false; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
664 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
665 if (session != null) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
666 session.close(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
667 session = null; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
668 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
669 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
670 if (connection != null) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
671 connection.close(); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
672 connection = null; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
673 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
674 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
675 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
676 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
677 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
678 public void setDimensions(int columns, int rows, int width, int height) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
679 this.columns = columns; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
680 this.rows = rows; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
681 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
682 if (sessionOpen) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
683 try { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
684 session.resizePTY(columns, rows, width, height); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
685 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
686 catch (IOException e) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
687 Log.e(TAG, "Couldn't send resize PTY packet", e); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
688 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
689 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
690 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
691 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
692 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
693 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
694 public void setOptions(Map<String, String> options) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
695 if (options.containsKey("compression")) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
696 compression = Boolean.parseBoolean(options.get("compression")); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
697 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
698 if (options.containsKey("httpproxy")) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
699 httpproxy = options.get("httpproxy"); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
700 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
701 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
702 |
0 | 703 @Override |
704 public Map<String, String> getOptions() { | |
705 Map<String, String> options = new HashMap<String, String>(); | |
706 options.put("compression", Boolean.toString(compression)); | |
707 | |
708 if (httpproxy != null) | |
709 options.put("httpproxy", httpproxy); | |
710 | |
711 return options; | |
712 } | |
713 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
714 |
0 | 715 @Override |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
716 public void setCompression(boolean compression) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
717 this.compression = compression; |
0 | 718 } |
719 | |
720 | |
721 @Override | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
722 public void setHttpproxy(String httpproxy) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
723 this.httpproxy = httpproxy; |
0 | 724 } |
725 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
726 |
0 | 727 @Override |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
728 public void setUseAuthAgent(String useAuthAgent) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
729 this.useAuthAgent = useAuthAgent; |
0 | 730 } |
731 | |
732 @Override | |
733 public boolean canForwardPorts() { | |
734 return true; | |
735 } | |
736 | |
737 @Override | |
738 public List<PortForwardBean> getPortForwards() { | |
739 return portForwards; | |
740 } | |
741 | |
742 @Override | |
743 public boolean addPortForward(PortForwardBean portForward) { | |
744 return portForwards.add(portForward); | |
745 } | |
746 | |
747 @Override | |
748 public boolean removePortForward(PortForwardBean portForward) { | |
749 // Make sure we don't have a phantom forwarder. | |
750 disablePortForward(portForward); | |
751 return portForwards.remove(portForward); | |
752 } | |
753 | |
754 @Override | |
755 public boolean enablePortForward(PortForwardBean portForward) { | |
756 if (!portForwards.contains(portForward)) { | |
757 Log.e(TAG, "Attempt to enable port forward not in list"); | |
758 return false; | |
759 } | |
760 | |
761 if (!authenticated) | |
762 return false; | |
763 | |
764 if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { | |
765 LocalPortForwarder lpf = null; | |
766 | |
767 try { | |
768 lpf = connection.createLocalPortForwarder( | |
769 new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort()), | |
770 portForward.getDestAddr(), portForward.getDestPort()); | |
771 } | |
772 catch (Exception e) { | |
773 Log.e(TAG, "Could not create local port forward", e); | |
774 return false; | |
775 } | |
776 | |
777 if (lpf == null) { | |
778 Log.e(TAG, "returned LocalPortForwarder object is null"); | |
779 return false; | |
780 } | |
781 | |
782 portForward.setIdentifier(lpf); | |
783 portForward.setEnabled(true); | |
784 return true; | |
785 } | |
786 else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { | |
787 try { | |
788 connection.requestRemotePortForwarding("", portForward.getSourcePort(), portForward.getDestAddr(), portForward.getDestPort()); | |
789 } | |
790 catch (Exception e) { | |
791 Log.e(TAG, "Could not create remote port forward", e); | |
792 return false; | |
793 } | |
794 | |
795 portForward.setEnabled(true); | |
796 return true; | |
797 } | |
798 else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { | |
799 DynamicPortForwarder dpf = null; | |
800 | |
801 try { | |
802 dpf = connection.createDynamicPortForwarder( | |
803 new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort())); | |
804 } | |
805 catch (Exception e) { | |
806 Log.e(TAG, "Could not create dynamic port forward", e); | |
807 return false; | |
808 } | |
809 | |
810 portForward.setIdentifier(dpf); | |
811 portForward.setEnabled(true); | |
812 return true; | |
813 } | |
814 else { | |
815 // Unsupported type | |
816 Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); | |
817 return false; | |
818 } | |
819 } | |
820 | |
821 @Override | |
822 public boolean disablePortForward(PortForwardBean portForward) { | |
823 if (!portForwards.contains(portForward)) { | |
824 Log.e(TAG, "Attempt to disable port forward not in list"); | |
825 return false; | |
826 } | |
827 | |
828 if (!authenticated) | |
829 return false; | |
830 | |
831 if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { | |
832 LocalPortForwarder lpf = null; | |
833 lpf = (LocalPortForwarder)portForward.getIdentifier(); | |
834 | |
835 if (!portForward.isEnabled() || lpf == null) { | |
836 Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); | |
837 return false; | |
838 } | |
839 | |
840 portForward.setEnabled(false); | |
841 | |
842 try { | |
843 lpf.close(); | |
844 } | |
845 catch (IOException e) { | |
846 Log.e(TAG, "Could not stop local port forwarder, setting enabled to false", e); | |
847 return false; | |
848 } | |
849 | |
850 return true; | |
851 } | |
852 else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { | |
853 portForward.setEnabled(false); | |
854 | |
855 try { | |
856 connection.cancelRemotePortForwarding(portForward.getSourcePort()); | |
857 } | |
858 catch (IOException e) { | |
859 Log.e(TAG, "Could not stop remote port forwarding, setting enabled to false", e); | |
860 return false; | |
861 } | |
862 | |
863 return true; | |
864 } | |
865 else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { | |
866 DynamicPortForwarder dpf = null; | |
867 dpf = (DynamicPortForwarder)portForward.getIdentifier(); | |
868 | |
869 if (!portForward.isEnabled() || dpf == null) { | |
870 Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); | |
871 return false; | |
872 } | |
873 | |
874 portForward.setEnabled(false); | |
875 | |
876 try { | |
877 dpf.close(); | |
878 } | |
879 catch (IOException e) { | |
880 Log.e(TAG, "Could not stop dynamic port forwarder, setting enabled to false", e); | |
881 return false; | |
882 } | |
883 | |
884 return true; | |
885 } | |
886 else { | |
887 // Unsupported type | |
888 Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); | |
889 return false; | |
890 } | |
891 } | |
892 | |
893 @Override | |
894 public boolean canTransferFiles() { | |
895 return true; | |
896 } | |
897 | |
898 @Override | |
899 public boolean downloadFile(String remoteFile, String localFolder) { | |
900 try { | |
901 SCPClient client = new SCPClient(connection); | |
902 | |
903 if (localFolder == null || localFolder == "") | |
904 localFolder = Environment.getExternalStorageDirectory().getAbsolutePath(); | |
905 | |
906 File dir = new File(localFolder); | |
907 dir.mkdirs(); | |
908 client.get(remoteFile, localFolder); | |
909 return true; | |
910 } | |
911 catch (IOException e) { | |
912 Log.e(TAG, "Could not download remote file", e); | |
913 return false; | |
914 } | |
915 } | |
916 | |
917 @Override | |
918 public boolean uploadFile(String localFile, String remoteFile, | |
919 String remoteFolder, String mode) { | |
920 try { | |
921 SCPClient client = new SCPClient(connection); | |
922 | |
923 if (remoteFolder == null) | |
924 remoteFolder = ""; | |
925 | |
926 if (remoteFile == null || remoteFile == "") | |
927 client.put(localFile, remoteFolder, mode); | |
928 else | |
929 client.put(localFile, remoteFile, remoteFolder, mode); | |
930 | |
931 return true; | |
932 } | |
933 catch (IOException e) { | |
934 Log.e(TAG, "Could not upload local file", e); | |
935 return false; | |
936 } | |
937 } | |
938 | |
939 | |
940 @Override | |
941 public int getDefaultPort() { | |
942 return DEFAULT_PORT; | |
943 } | |
944 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
945 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
946 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
947 public boolean isConnected() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
948 return connected; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
949 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
950 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
951 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
952 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
953 public boolean isSessionOpen() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
954 return sessionOpen; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
955 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
956 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
957 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
958 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
959 public boolean isAuthenticated() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
960 return authenticated; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
961 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
962 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
963 |
0 | 964 @Override |
965 public String getDefaultNickname(String username, String hostname, int port) { | |
966 if (port == DEFAULT_PORT) { | |
967 return String.format(Locale.US, "%s@%s", username, hostname); | |
968 } | |
969 else { | |
970 return String.format(Locale.US, "%s@%s:%d", username, hostname, port); | |
971 } | |
972 } | |
973 | |
974 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
975 @Override |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
976 public void getSelectionArgs(Uri uri, Map<String, String> selection) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
977 selection.put(HostDatabase.FIELD_HOST_PROTOCOL, PROTOCOL); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
978 selection.put(HostDatabase.FIELD_HOST_NICKNAME, uri.getFragment()); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
979 selection.put(HostDatabase.FIELD_HOST_HOSTNAME, uri.getHost()); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
980 int port = uri.getPort(); |
0 | 981 |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
982 if (port < 0) |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
983 port = DEFAULT_PORT; |
0 | 984 |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
985 selection.put(HostDatabase.FIELD_HOST_PORT, Integer.toString(port)); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
986 selection.put(HostDatabase.FIELD_HOST_USERNAME, uri.getUserInfo()); |
0 | 987 } |
988 | |
989 | |
990 @Override | |
991 public HostBean createHost(Uri uri) { | |
992 HostBean host = new HostBean(); | |
993 host.setProtocol(PROTOCOL); | |
994 host.setHostname(uri.getHost()); | |
995 int port = uri.getPort(); | |
996 | |
997 if (port < 0) | |
998 port = DEFAULT_PORT; | |
999 | |
1000 host.setPort(port); | |
1001 host.setUsername(uri.getUserInfo()); | |
1002 String nickname = uri.getFragment(); | |
1003 | |
1004 if (nickname == null || nickname.length() == 0) { | |
1005 host.setNickname(getDefaultNickname(host.getUsername(), | |
1006 host.getHostname(), host.getPort())); | |
1007 } | |
1008 else { | |
1009 host.setNickname(uri.getFragment()); | |
1010 } | |
1011 | |
1012 return host; | |
1013 } | |
1014 | |
1015 | |
12 | 1016 public String getFormatHint(Context context) { |
0 | 1017 return String.format("%s@%s:%s", |
1018 context.getString(R.string.format_username), | |
1019 context.getString(R.string.format_hostname), | |
1020 context.getString(R.string.format_port)); | |
1021 } | |
1022 | |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1023 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1024 /** |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1025 * @return do we use the network |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1026 */ |
0 | 1027 @Override |
31
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1028 public boolean usesNetwork() { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1029 return true; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1030 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1031 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1032 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1033 /** |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1034 * Handle challenges from keyboard-interactive authentication mode. |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1035 */ |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1036 public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1037 interactiveCanContinue = true; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1038 String[] responses = new String[numPrompts]; |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1039 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1040 for (int i = 0; i < numPrompts; i++) { |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1041 // request response from user for each prompt |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1042 responses[i] = bridge.promptHelper.requestPasswordPrompt(instruction, prompt[i]); |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1043 } |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1044 |
139394237973
start tn5250 integration
Carl Byington <carl@five-ten-sg.com>
parents:
12
diff
changeset
|
1045 return responses; |
0 | 1046 } |
1047 | |
1048 public Map<String, byte[]> retrieveIdentities() { | |
1049 Map<String, byte[]> pubKeys = new HashMap<String, byte[]> (manager.loadedKeypairs.size()); | |
1050 | |
1051 for (Entry<String, KeyHolder> entry : manager.loadedKeypairs.entrySet()) { | |
1052 KeyPair pair = entry.getValue().pair; | |
1053 | |
1054 try { | |
1055 PrivateKey privKey = pair.getPrivate(); | |
1056 | |
1057 if (privKey instanceof RSAPrivateKey) { | |
1058 RSAPublicKey pubkey = (RSAPublicKey) pair.getPublic(); | |
1059 pubKeys.put(entry.getKey(), RSASHA1Verify.encodeSSHRSAPublicKey(pubkey)); | |
1060 } | |
1061 else if (privKey instanceof DSAPrivateKey) { | |
1062 DSAPublicKey pubkey = (DSAPublicKey) pair.getPublic(); | |
1063 pubKeys.put(entry.getKey(), DSASHA1Verify.encodeSSHDSAPublicKey(pubkey)); | |
1064 } | |
1065 else | |
1066 continue; | |
1067 } | |
1068 catch (IOException e) { | |
1069 continue; | |
1070 } | |
1071 } | |
1072 | |
1073 return pubKeys; | |
1074 } | |
1075 | |
1076 public KeyPair getKeyPair(byte[] publicKey) { | |
1077 String nickname = manager.getKeyNickname(publicKey); | |
1078 | |
1079 if (nickname == null) | |
1080 return null; | |
1081 | |
1082 if (useAuthAgent.equals(HostDatabase.AUTHAGENT_NO)) { | |
1083 Log.e(TAG, ""); | |
1084 return null; | |
1085 } | |
1086 else if (useAuthAgent.equals(HostDatabase.AUTHAGENT_CONFIRM) || | |
1087 manager.loadedKeypairs.get(nickname).bean.isConfirmUse()) { | |
1088 if (!promptForPubkeyUse(nickname)) | |
1089 return null; | |
1090 } | |
1091 | |
1092 return manager.getKey(nickname); | |
1093 } | |
1094 | |
1095 private boolean promptForPubkeyUse(String nickname) { | |
1096 Boolean result = bridge.promptHelper.requestBooleanPrompt(null, | |
1097 manager.res.getString(R.string.prompt_allow_agent_to_use_key, | |
1098 nickname)); | |
1099 return result; | |
1100 } | |
1101 | |
1102 public boolean addIdentity(KeyPair pair, String comment, boolean confirmUse, int lifetime) { | |
1103 PubkeyBean pubkey = new PubkeyBean(); | |
1104 // pubkey.setType(PubkeyDatabase.KEY_TYPE_IMPORTED); | |
1105 pubkey.setNickname(comment); | |
1106 pubkey.setConfirmUse(confirmUse); | |
1107 pubkey.setLifetime(lifetime); | |
1108 manager.addKey(pubkey, pair); | |
1109 return true; | |
1110 } | |
1111 | |
1112 public boolean removeAllIdentities() { | |
1113 manager.loadedKeypairs.clear(); | |
1114 return true; | |
1115 } | |
1116 | |
1117 public boolean removeIdentity(byte[] publicKey) { | |
1118 return manager.removeKey(publicKey); | |
1119 } | |
1120 | |
1121 public boolean isAgentLocked() { | |
1122 return agentLockPassphrase != null; | |
1123 } | |
1124 | |
1125 public boolean requestAgentUnlock(String unlockPassphrase) { | |
1126 if (agentLockPassphrase == null) | |
1127 return false; | |
1128 | |
1129 if (agentLockPassphrase.equals(unlockPassphrase)) | |
1130 agentLockPassphrase = null; | |
1131 | |
1132 return agentLockPassphrase == null; | |
1133 } | |
1134 | |
1135 public boolean setAgentLock(String lockPassphrase) { | |
1136 if (agentLockPassphrase != null) | |
1137 return false; | |
1138 | |
1139 agentLockPassphrase = lockPassphrase; | |
1140 return true; | |
1141 } | |
1142 | |
1143 } |