1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
3 Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in
13 the documentation and/or other materials provided with the distribution.
15 3. The names of the authors may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
21 INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 package com.jcraft.jsch;
35 class KnownHosts implements HostKeyRepository{
36 private static final String _known_hosts="known_hosts";
39 static final int SSHDSS=0;
40 static final int SSHRSA=1;
41 static final int UNKNOWN=2;
44 private JSch jsch=null;
45 private String known_hosts=null;
46 private java.util.Vector pool=null;
48 private MAC hmacsha1=null;
50 KnownHosts(JSch jsch){
53 pool=new java.util.Vector();
56 void setKnownHosts(String foo) throws JSchException{
59 FileInputStream fis=new FileInputStream(foo);
62 catch(FileNotFoundException e){
65 void setKnownHosts(InputStream foo) throws JSchException{
66 pool.removeAllElements();
67 StringBuffer sb=new StringBuffer();
76 byte[] buf=new byte[1024];
84 if(bufl==0){ break loop; }
87 if(j==0x0d){ continue; }
90 if(bufl>1024*10) break; // too long...
91 byte[] newbuf=new byte[buf.length*2];
92 System.arraycopy(buf, 0, newbuf, 0, buf.length);
101 if(i==' '||i=='\t'){ j++; continue; }
103 addInvalidLine(Util.byte2str(buf, 0, bufl));
109 addInvalidLine(Util.byte2str(buf, 0, bufl));
116 if(i==0x20 || i=='\t'){ break; }
120 if(j>=bufl || host.length()==0){
121 addInvalidLine(Util.byte2str(buf, 0, bufl));
129 if(i==0x20 || i=='\t'){ break; }
132 if(sb.toString().equals("ssh-dss")){ type=HostKey.SSHDSS; }
133 else if(sb.toString().equals("ssh-rsa")){ type=HostKey.SSHRSA; }
136 addInvalidLine(Util.byte2str(buf, 0, bufl));
143 if(i==0x0d){ continue; }
144 if(i==0x0a){ break; }
149 addInvalidLine(Util.byte2str(buf, 0, bufl));
153 //System.err.println(host);
154 //System.err.println("|"+key+"|");
157 hk = new HashedHostKey(host, type,
158 Util.fromBase64(Util.str2byte(key), 0,
164 throw new JSchException("KnownHosts: invalid format");
168 if(e instanceof JSchException)
169 throw (JSchException)e;
170 if(e instanceof Throwable)
171 throw new JSchException(e.toString(), (Throwable)e);
172 throw new JSchException(e.toString());
175 private void addInvalidLine(String line) throws JSchException {
176 HostKey hk = new HostKey(line, HostKey.UNKNOWN, null);
179 String getKnownHostsFile(){ return known_hosts; }
180 public String getKnownHostsRepositoryID(){ return known_hosts; }
182 public int check(String host, byte[] key){
183 int result=NOT_INCLUDED;
188 int type=getType(key);
192 for(int i=0; i<pool.size(); i++){
193 hk=(HostKey)(pool.elementAt(i));
194 if(hk.isMatched(host) && hk.type==type){
195 if(Util.array_equals(hk.key, key)){
205 if(result==NOT_INCLUDED &&
206 host.startsWith("[") &&
209 return check(host.substring(1, host.indexOf("]:")), key);
214 public void add(HostKey hostkey, UserInfo userinfo){
215 int type=hostkey.type;
216 String host=hostkey.getHost();
217 byte[] key=hostkey.key;
221 for(int i=0; i<pool.size(); i++){
222 hk=(HostKey)(pool.elementAt(i));
223 if(hk.isMatched(host) && hk.type==type){
225 if(Util.array_equals(hk.key, key)){ return; }
226 if(hk.host.equals(host)){
231 hk.host=deleteSubString(hk.host, host);
243 String bar=getKnownHostsRepositoryID();
246 File goo=new File(bar);
250 foo=userinfo.promptYesNo(bar+" does not exist.\n"+
251 "Are you sure you want to create it?"
253 goo=goo.getParentFile();
254 if(foo && goo!=null && !goo.exists()){
255 foo=userinfo.promptYesNo("The parent directory "+goo+" does not exist.\n"+
256 "Are you sure you want to create it?"
260 userinfo.showMessage(goo+" has not been created.");
264 userinfo.showMessage(goo+" has been succesfully created.\nPlease check its access permission.");
268 if(goo==null)foo=false;
275 catch(Exception e){ System.err.println("sync known_hosts: "+e); }
280 public HostKey[] getHostKey(){
281 return getHostKey(null, null);
283 public HostKey[] getHostKey(String host, String type){
286 for(int i=0; i<pool.size(); i++){
287 HostKey hk=(HostKey)pool.elementAt(i);
288 if(hk.type==HostKey.UNKNOWN) continue;
290 (hk.isMatched(host) &&
291 (type==null || hk.getType().equals(type)))){
295 if(count==0)return null;
296 HostKey[] foo=new HostKey[count];
298 for(int i=0; i<pool.size(); i++){
299 HostKey hk=(HostKey)pool.elementAt(i);
300 if(hk.type==HostKey.UNKNOWN) continue;
302 (hk.isMatched(host) &&
303 (type==null || hk.getType().equals(type)))){
310 public void remove(String host, String type){
311 remove(host, type, null);
313 public void remove(String host, String type, byte[] key){
316 for(int i=0; i<pool.size(); i++){
317 HostKey hk=(HostKey)(pool.elementAt(i));
319 (hk.isMatched(host) &&
320 (type==null || (hk.getType().equals(type) &&
321 (key==null || Util.array_equals(key, hk.key)))))){
322 String hosts=hk.getHost();
323 if(hosts.equals(host) ||
324 ((hk instanceof HashedHostKey) &&
325 ((HashedHostKey)hk).isHashed())){
326 pool.removeElement(hk);
329 hk.host=deleteSubString(hosts, host);
336 try{sync();}catch(Exception e){};
340 protected void sync() throws IOException {
341 if(known_hosts!=null)
344 protected synchronized void sync(String foo) throws IOException {
345 if(foo==null) return;
346 FileOutputStream fos=new FileOutputStream(foo);
351 private static final byte[] space={(byte)0x20};
352 private static final byte[] cr=Util.str2byte("\n");
353 void dump(OutputStream out) throws IOException {
357 for(int i=0; i<pool.size(); i++){
358 hk=(HostKey)(pool.elementAt(i));
360 String host=hk.getHost();
361 String type=hk.getType();
362 if(type.equals("UNKNOWN")){
363 out.write(Util.str2byte(host));
367 out.write(Util.str2byte(host));
369 out.write(Util.str2byte(type));
371 out.write(Util.str2byte(hk.getKey()));
377 System.err.println(e);
380 private int getType(byte[] key){
381 if(key[8]=='d') return HostKey.SSHDSS;
382 if(key[8]=='r') return HostKey.SSHRSA;
383 return HostKey.UNKNOWN;
385 private String deleteSubString(String hosts, String host){
387 int hostlen=host.length();
388 int hostslen=hosts.length();
391 j=hosts.indexOf(',', i);
393 if(!host.equals(hosts.substring(i, j))){
397 return hosts.substring(0, i)+hosts.substring(j+1);
399 if(hosts.endsWith(host) && hostslen-i==hostlen){
400 return hosts.substring(0, (hostlen==hostslen) ? 0 :hostslen-hostlen-1);
405 private synchronized MAC getHMACSHA1(){
408 Class c=Class.forName(jsch.getConfig("hmac-sha1"));
409 hmacsha1=(MAC)(c.newInstance());
412 System.err.println("hmacsha1: "+e);
418 HostKey createHashedHostKey(String host, byte[]key) throws JSchException {
419 HashedHostKey hhk=new HashedHostKey(host, key);
423 class HashedHostKey extends HostKey{
424 private static final String HASH_MAGIC="|1|";
425 private static final String HASH_DELIM="|";
427 private boolean hashed=false;
432 HashedHostKey(String host, byte[] key) throws JSchException {
433 this(host, GUESS, key);
435 HashedHostKey(String host, int type, byte[] key) throws JSchException {
436 super(host, type, key);
437 if(this.host.startsWith(HASH_MAGIC) &&
438 this.host.substring(HASH_MAGIC.length()).indexOf(HASH_DELIM)>0){
439 String data=this.host.substring(HASH_MAGIC.length());
440 String _salt=data.substring(0, data.indexOf(HASH_DELIM));
441 String _hash=data.substring(data.indexOf(HASH_DELIM)+1);
442 salt=Util.fromBase64(Util.str2byte(_salt), 0, _salt.length());
443 hash=Util.fromBase64(Util.str2byte(_hash), 0, _hash.length());
444 if(salt.length!=20 || // block size of hmac-sha1
454 boolean isMatched(String _host){
456 return super.isMatched(_host);
458 MAC macsha1=getHMACSHA1();
460 synchronized(macsha1){
462 byte[] foo=Util.str2byte(_host);
463 macsha1.update(foo, 0, foo.length);
464 byte[] bar=new byte[macsha1.getBlockSize()];
465 macsha1.doFinal(bar, 0);
466 return Util.array_equals(hash, bar);
470 System.out.println(e);
482 MAC macsha1=getHMACSHA1();
484 Random random=Session.random;
485 synchronized(random){
486 salt=new byte[macsha1.getBlockSize()];
487 random.fill(salt, 0, salt.length);
491 synchronized(macsha1){
493 byte[] foo=Util.str2byte(host);
494 macsha1.update(foo, 0, foo.length);
495 hash=new byte[macsha1.getBlockSize()];
496 macsha1.doFinal(hash, 0);
501 host=HASH_MAGIC+Util.byte2str(Util.toBase64(salt, 0, salt.length))+
502 HASH_DELIM+Util.byte2str(Util.toBase64(hash, 0, hash.length));