1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 package com.sshtools.common.automate;
27
28 import com.sshtools.j2ssh.SftpClient;
29 import com.sshtools.j2ssh.transport.publickey.SshPublicKey;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33
34 import java.io.ByteArrayInputStream;
35 import java.io.ByteArrayOutputStream;
36 import java.io.IOException;
37
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Vector;
41
42
43 /**
44 *
45 *
46 * @author $author$
47 * @version $Revision: 1.19 $
48 */
49 public class RemoteIdentification {
50 /** */
51 public static final int ADD_AUTHORIZEDKEY = 1;
52
53 /** */
54 public static final int REMOVE_AUTHORIZEDKEY = 2;
55 private String defaultName;
56 private Vector rules = new Vector();
57 private Class authorizedKeysFormat;
58 private String defaultPath;
59
60 /** */
61 protected Log log = LogFactory.getLog(RemoteIdentification.class);
62
63 /**
64 * Creates a new RemoteIdentification object.
65 *
66 * @param defaultName
67 */
68 public RemoteIdentification(String defaultName) {
69 this.defaultName = defaultName;
70 }
71
72 /**
73 *
74 *
75 * @return
76 */
77 protected List getRules() {
78 return rules;
79 }
80
81 /**
82 *
83 *
84 * @return
85 */
86 public String getDefaultName() {
87 return defaultName;
88 }
89
90 /**
91 *
92 *
93 * @param ident
94 *
95 * @return
96 *
97 * @throws RemoteIdentificationException
98 */
99 public String getName(String ident) throws RemoteIdentificationException {
100 boolean pass = false;
101 Iterator it = rules.iterator();
102 Vector passed = new Vector();
103 RemoteIdentificationRule rule;
104 String rulename = null;
105
106 // Check all the rules
107 while (it.hasNext()) {
108 rule = (RemoteIdentificationRule) it.next();
109
110 if (rule.testRule(ident)) {
111 passed.add(rule);
112 }
113 }
114
115 if (passed.size() > 0) {
116 // Select the highest priority rule where 0=highest 10=lowest
117 it = passed.iterator();
118
119 RemoteIdentificationRule ret = null;
120
121 while (it.hasNext()) {
122 rule = (RemoteIdentificationRule) it.next();
123
124 if (ret == null) {
125 ret = rule;
126 } else {
127 if (rule.getPriority() < ret.getPriority()) {
128 ret = rule;
129 }
130 }
131 }
132
133 if (ret.getName() != null) {
134 return ret.getName();
135 } else {
136 return defaultName;
137 }
138 } else {
139 throw new RemoteIdentificationException(
140 "No rules exist to identify the remote host with ident string " +
141 ident);
142 }
143 }
144
145 /**
146 *
147 *
148 * @param rule
149 */
150 protected void addRule(RemoteIdentificationRule rule) {
151 rules.add(rule);
152 }
153
154 /**
155 *
156 *
157 * @param ident
158 *
159 * @return
160 */
161 protected boolean testRules(String ident) {
162 boolean pass = false;
163 Iterator it = rules.iterator();
164 RemoteIdentificationRule rule;
165
166 while (it.hasNext() && !pass) {
167 rule = (RemoteIdentificationRule) it.next();
168 pass = rule.testRule(ident);
169 }
170
171 return pass;
172 }
173
174 /**
175 *
176 *
177 * @param implementationClass
178 */
179 protected void setAuthorizedKeysFormat(Class implementationClass) {
180 authorizedKeysFormat = implementationClass;
181 }
182
183 /**
184 *
185 *
186 * @param defaultPath
187 */
188 protected void setAuthorizedKeysDefaultPath(String defaultPath) {
189 this.defaultPath = defaultPath;
190 }
191
192 /**
193 *
194 *
195 * @return
196 */
197 public String getAuthorizedKeysDefaultPath() {
198 return defaultPath;
199 }
200
201 /**
202 *
203 *
204 * @return
205 *
206 * @throws RemoteIdentificationException
207 */
208 public AuthorizedKeysFormat getAuthorizedKeysFormat()
209 throws RemoteIdentificationException {
210 try {
211 if (authorizedKeysFormat != null) {
212 return (AuthorizedKeysFormat) authorizedKeysFormat.newInstance();
213 } else {
214 throw new RemoteIdentificationException(
215 "There is no authorized keys format set for this remote id");
216 }
217 } catch (Exception ex) {
218 throw new RemoteIdentificationException("Failed to instansiate " +
219 authorizedKeysFormat.getName());
220 }
221 }
222
223 /**
224 *
225 *
226 * @param sftp
227 * @param serverId
228 * @param system
229 * @param username
230 * @param pk
231 * @param authorizationFile
232 * @param mode
233 *
234 * @return
235 *
236 * @throws RemoteIdentificationException
237 */
238 public boolean configureUserAccess(SftpClient sftp, String serverId,
239 String system, String username, SshPublicKey pk,
240 String authorizationFile, int mode)
241 throws RemoteIdentificationException {
242 Vector keys = new Vector();
243 keys.add(pk);
244
245 return configureUserAccess(sftp, serverId, system, username, keys,
246 authorizationFile, mode);
247 }
248
249 /**
250 *
251 *
252 * @param sftp
253 * @param serverId
254 * @param system
255 * @param username
256 * @param keys
257 * @param authorizationFile
258 * @param mode
259 *
260 * @return
261 *
262 * @throws RemoteIdentificationException
263 */
264 public boolean configureUserAccess(final SftpClient sftp,
265 final String serverId, String system, String username, List keys,
266 String authorizationFile, int mode)
267 throws RemoteIdentificationException {
268 try {
269 if (sftp.isClosed()) {
270 throw new RemoteIdentificationException(
271 "SFTP connection must be open");
272 }
273
274 if (authorizationFile == null) {
275 throw new RemoteIdentificationException(
276 "authorization file cannot be null");
277 }
278
279 if ((mode != ADD_AUTHORIZEDKEY) && (mode != REMOVE_AUTHORIZEDKEY)) {
280 throw new RemoteIdentificationException(
281 "Invalid configuration mode specifed in call to configureUserAccess");
282 }
283
284 AuthorizedKeys authorizedKeys;
285 authorizationFile.replace('\\', '/');
286
287 final String directory = ((authorizationFile.lastIndexOf("/") > 0)
288 ? authorizationFile.substring(0,
289 authorizationFile.lastIndexOf("/") + 1) : "");
290
291 try {
292 // Remove the old backup - ignore the error
293 try {
294 sftp.rm(authorizationFile + ".bak");
295 } catch (IOException ex) {
296 }
297
298 // Change the current authorization file to the backup
299 sftp.rename(authorizationFile, authorizationFile + ".bak");
300 log.info("Opening existing authorized keys file from " +
301 authorizationFile + ".bak");
302
303 ByteArrayOutputStream out = new ByteArrayOutputStream();
304 sftp.get(authorizationFile + ".bak", out);
305
306 byte[] backup = out.toByteArray();
307 out.close();
308
309 // Obtain the current authoized keys settings
310 log.info("Parsing authorized keys file");
311 authorizedKeys = AuthorizedKeys.parse(backup, serverId, system,
312 new AuthorizedKeysFileLoader() {
313 public byte[] loadFile(String filename)
314 throws IOException {
315 ByteArrayOutputStream out = new ByteArrayOutputStream();
316 sftp.get(directory + filename, out);
317 out.close();
318
319 return out.toByteArray();
320 }
321 });
322 } catch (IOException ioe) {
323 // Could not open so create a new file
324 authorizedKeys = new AuthorizedKeys();
325 } catch (RemoteIdentificationException rie) {
326 throw new RemoteIdentificationException(
327 "Open3SP cannot identify the remote host.\n" +
328 "Please email support@open3sp.org with specifying 'remote identification' in the subject and supplying the server type and the follwing data '" +
329 serverId + "'");
330 }
331
332 log.info("Updating authorized keys file");
333
334 // Check the existing keys and add any that are not present
335 SshPublicKey pk;
336
337 for (Iterator x = keys.iterator(); x.hasNext();) {
338 pk = (SshPublicKey) x.next();
339
340 if (!authorizedKeys.containsKey(pk) &&
341 (mode == ADD_AUTHORIZEDKEY)) {
342 authorizedKeys.addKey(username, pk);
343 } else if (authorizedKeys.containsKey(pk) &&
344 (mode == REMOVE_AUTHORIZEDKEY)) {
345 authorizedKeys.removeKey(pk);
346 }
347 }
348
349 // Verfiy that the directory exists?
350 log.info("Verifying directory " + directory);
351
352 int umask = sftp.umask(0022);
353 sftp.mkdirs(directory);
354
355 // Output the new file
356 log.info("Writing new authorized keys file to " +
357 authorizationFile);
358
359 ByteArrayOutputStream out = new ByteArrayOutputStream();
360
361 // Output the authorization file to a ByteArrayOutputStream
362 out.write(AuthorizedKeys.create(authorizedKeys, serverId, system,
363 new AuthorizedKeysFileSaver() {
364 public void saveFile(String filename, byte[] filedata)
365 throws IOException {
366 //SftpFile file = null;
367 ByteArrayInputStream in = null;
368
369 try {
370 in = new ByteArrayInputStream(filedata);
371 sftp.put(in, directory + filename);
372 } catch (IOException ex) {
373 log.info("Error writing public key file to server" +
374 filename, ex);
375 } finally {
376 if (in != null) {
377 in.close();
378 }
379 }
380 }
381 }));
382 out.close();
383
384 // Copy the new authorisation file to the server
385 ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
386 sftp.umask(0133);
387 sftp.put(in, authorizationFile);
388 sftp.umask(umask);
389
390 return true;
391 } catch (IOException ioe) {
392 throw new RemoteIdentificationException(ioe.getMessage());
393 } catch (RemoteIdentificationException rie) {
394 throw new RemoteIdentificationException(
395 "SSHTools cannot identify the remote host.\n" +
396 "Please email support@sshtools.com specifying 'remote identification' in the subject, supplying the server type and the following data: '" +
397 serverId + "'");
398 }
399 }
400 }