51
51
* An implementation that uses native SSH agent installed on a system.
52
52
*/
53
53
public class ExecRemoteAgent implements RemoteAgent {
54
- private final String AuthSocketVar = "SSH_AUTH_SOCK" ;
55
- private final String AgentPidVar = "SSH_AGENT_PID" ;
54
+ private static final String AuthSocketVar = "SSH_AUTH_SOCK" ;
55
+ private static final String AgentPidVar = "SSH_AGENT_PID" ;
56
56
57
57
/** Process builder keeping environment for all ExecRemoteAgent related processes. */
58
58
private final ProcessBuilder processBuilder ;
@@ -117,11 +117,13 @@ public void addIdentity(String privateKey, final String passphrase, String comme
117
117
processBuilder .environment ().put ("DISPLAY" , ":0" ); // just to force using SSH_ASKPASS
118
118
processBuilder .environment ().put ("SSH_ASKPASS" , askpass .getPath ());
119
119
120
- // TODO: kill agent in case of wrong password
121
-
122
120
final Process sshAdd = execProcess ("ssh-add " + keyFile .getPath ());
123
121
124
- IOUtils .copy (sshAdd .getErrorStream (), listener .getLogger ());
122
+ String errorString = streamToString (sshAdd .getErrorStream ());
123
+ if (!errorString .isEmpty ()) {
124
+ errorString += "Check the passphrase for the private key." ;
125
+ listener .getLogger ().println (errorString );
126
+ }
125
127
IOUtils .copy (sshAdd .getInputStream (), listener .getLogger ());
126
128
127
129
try {
@@ -135,7 +137,7 @@ public void addIdentity(String privateKey, final String passphrase, String comme
135
137
processBuilder .environment ().remove ("DISPLAY" );
136
138
processBuilder .environment ().remove ("SSH_PASSPHRASE" );
137
139
138
- askpass .delete ();
140
+ askpass .delete (); // the ASKPASS script is self-deleting, anyway rather try to delete it in case of some error
139
141
140
142
if (!keyFile .delete ()) {
141
143
listener .getLogger ().println ("ExecRemoteAgent::addIdentity - could NOT delete a temp file with a private key!" );
@@ -167,16 +169,19 @@ private Process execProcess(String command) throws IOException {
167
169
return process ;
168
170
}
169
171
172
+ private String streamToString (InputStream is ) throws IOException {
173
+ ByteArrayOutputStream os = new ByteArrayOutputStream ();
174
+ IOUtils .copy (is , os );
175
+ return os .toString ();
176
+ }
177
+
170
178
/**
171
179
* Parses ssh-agent output.
172
180
*/
173
181
private Map <String ,String > parseAgentEnv (Process agent ) throws Exception {
174
182
Map <String , String > env = new HashMap <String , String >();
175
183
176
- InputStream agentOutputReader = agent .getInputStream ();
177
- ByteArrayOutputStream agentOutputStream = new ByteArrayOutputStream ();
178
- IOUtils .copy (agentOutputReader , agentOutputStream );
179
- String agentOutput = agentOutputStream .toString ();
184
+ String agentOutput = streamToString (agent .getInputStream ());
180
185
181
186
// get SSH_AUTH_SOCK
182
187
env .put (AuthSocketVar , getAgentValue (agentOutput , AuthSocketVar ));
@@ -198,6 +203,9 @@ private String getAgentValue(String agentOutput, String envVar) {
198
203
return agentOutput .substring (pos , end );
199
204
}
200
205
206
+ /**
207
+ * Sets file's permissions to readable only for an owner.
208
+ */
201
209
private boolean setReadOnlyForOwner (File file ) {
202
210
boolean ok = file .setExecutable (false , false );
203
211
ok &= file .setWritable (false , false );
@@ -206,17 +214,24 @@ private boolean setReadOnlyForOwner(File file) {
206
214
return ok ;
207
215
}
208
216
217
+ /**
218
+ * Creates a self-deleting script for SSH_ASKPASS. Self-deleting to be able to detect a wrong passphrase.
219
+ */
209
220
private File createAskpassScript () throws IOException {
221
+ // TODO: assuming that ssh-add runs the script in shell even on Windows, not cmd
222
+ // for cmd following could work
223
+ // suffix = ".bat";
224
+ // script = "@ECHO %SSH_PASSPHRASE%\nDEL \"" + askpass.getAbsolutePath() + "\"\n";
225
+
210
226
final String suffix ;
211
227
final String script ;
212
- // TODO: assuming that ssh-add runs the script in shell, not cmd
228
+
213
229
suffix = ".sh" ;
214
- script = "#!/bin/sh\n echo $SSH_PASSPHRASE\n " ;
215
- // for cmd following should work
216
- // suffix = ".bat";
217
- // script = "@echo %SSH_PASSPHRASE%\n";
218
-
230
+
219
231
File askpass = File .createTempFile ("askpass_" , suffix );
232
+
233
+ script = "#!/bin/sh\n echo $SSH_PASSPHRASE\n rm " + askpass .getAbsolutePath ().replace ("\\ " , "\\ \\ " ) + "\n " ;
234
+
220
235
FileWriter askpassWriter = new FileWriter (askpass );
221
236
askpassWriter .write (script );
222
237
askpassWriter .close ();
0 commit comments