| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.team.internal.ccvs.ssh; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.math.BigInteger; |
| import java.util.Vector; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.team.internal.ccvs.ssh.Policy; |
| |
| /** |
| * I represent a database of known hosts usually placed in ~/.ssh/known_hosts |
| * on Unix/Linux systems. |
| * Currently, only RSA keys are supported, as these are the only keys we |
| * have to deal with during SSH1 key exchange. |
| */ |
| public class KnownHosts { |
| |
| private String filename; |
| |
| public KnownHosts(String filename) { |
| this.filename = filename; |
| } |
| |
| public KnownHosts() { |
| this.filename = KnownHosts.defaultFilename(); |
| } |
| |
| static String defaultFilename() { |
| if (!Platform.getOS().equals(Platform.OS_LINUX)) return internalFilename(); |
| String HOME = System.getProperty("user.home"); //$NON-NLS-1$ |
| if (HOME==null) return internalFilename(); |
| return HOME+"/.ssh/known_hosts"; //$NON-NLS-1$ |
| } |
| |
| private static String internalFilename() { |
| return SSHPlugin.getPlugin().getStateLocation().append("known_hosts").toOSString(); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Verify if the public key for the specified host is known. |
| * If the public key matches, return true. |
| * If the key does not match, return false. |
| * If the key is not listed in <code>known_hosts</code>, or |
| * <code>known_hosts</code> does not exist, assume we are connecting |
| * to the authentic server, add the key, and return true. |
| * @param e key exponent |
| * @param n key modulus |
| * @return boolean whether the key is correct |
| */ |
| public boolean verifyKey(String hostname, byte[] host_key_bits, BigInteger e, BigInteger n) { |
| FileReader f; |
| BigInteger nbits = new BigInteger(1, host_key_bits); |
| try { |
| f= new FileReader(filename); |
| } catch (FileNotFoundException ex) { |
| createHostFile(); |
| addHost(hostname, nbits, e, n); |
| return true; |
| } |
| BufferedReader r = new BufferedReader(f); |
| try { |
| String line; |
| while ((line = r.readLine()) != null) { |
| if (line.trim().length()==0) continue; |
| if (line.startsWith("#")) continue; //$NON-NLS-1$ |
| String[] tokens=subStrings(line); |
| if (tokens.length==4 && Character.isDigit(tokens[1].charAt(0)) && tokens[0].equalsIgnoreCase(hostname)) { |
| if (nbits.equals(new BigInteger(tokens[1])) && e.equals(new BigInteger(tokens[2])) && n.equals(new BigInteger(tokens[3]))) { |
| f.close(); |
| return true; |
| } else { |
| f.close(); |
| return false; |
| } |
| } |
| } |
| f.close(); |
| addHost(hostname, nbits, e, n); |
| return true; |
| } catch (IOException ex) { |
| SSHPlugin.log(IStatus.ERROR, Policy.bind("KnownHosts.8"), ex); //$NON-NLS-1$ |
| return false; |
| } |
| } |
| |
| /* |
| * Append the host key information to known_hosts. |
| * Always assume the file exists. |
| */ |
| void addHost(String hostname, BigInteger key_bits, BigInteger e, BigInteger n) { |
| try { |
| FileWriter w = new FileWriter(defaultFilename(), true); |
| w.write(Character.LINE_SEPARATOR); |
| w.write(hostname + " " + key_bits.toString(10) + " " + e.toString(10) + " " + n.toString(10)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| w.close(); |
| String message = Policy.bind("Client.addedHostKey", new String[] {hostname, defaultFilename()}); //$NON-NLS-1$ |
| SSHPlugin.log(IStatus.INFO, message, null); |
| } catch (IOException ex) { |
| SSHPlugin.log(IStatus.ERROR, Policy.bind("KnownHosts.9"), ex); //$NON-NLS-1$ |
| } |
| } |
| |
| /* |
| * Create the known_hosts file in the default location. |
| * Fail if the file can not be created (issue a warning in the log). |
| */ |
| void createHostFile() { |
| try { |
| File file = new File(defaultFilename()); |
| // Ensure the parent directory exists |
| File parentDir = file.getParentFile(); |
| parentDir.mkdirs(); |
| // Create the file |
| file.createNewFile(); |
| } catch (IOException ee) { |
| SSHPlugin.log(IStatus.ERROR, Policy.bind("KnownHosts.10"), ee); //$NON-NLS-1$ |
| } |
| |
| } |
| private static String[] subStrings(String s) { |
| Vector v = subStringsVector(s); |
| String[] substrings = new String[v.size()]; |
| v.copyInto(substrings); |
| return substrings; |
| } |
| private static Vector subStringsVector(String s) { |
| Vector v = new Vector(); |
| s = s.trim(); |
| if (s.length()==0) return v; |
| int first1 = s.indexOf(' '); |
| int first2 = s.indexOf('\t'); |
| int first; |
| if ((first1==-1)&&(first2==-1)) first=-1; |
| else if ((first1!=-1)&&(first2!=-1)) first = Math.min(first1, first2); |
| else if (first1!=-1) first=first1; else first=first2; |
| if (first==-1) { |
| v.add(s); |
| return v; |
| } |
| v.add(s.substring(0,first)); |
| v.addAll(subStringsVector(s.substring(first+1))); |
| return v; |
| } |
| } |