KnownHosts.java

Go to the documentation of this file.
00001 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
00002 /*
00003 Copyright (c) 2002-2011 ymnk, JCraft,Inc. All rights reserved.
00004 
00005 Redistribution and use in source and binary forms, with or without
00006 modification, are permitted provided that the following conditions are met:
00007 
00008   1. Redistributions of source code must retain the above copyright notice,
00009      this list of conditions and the following disclaimer.
00010 
00011   2. Redistributions in binary form must reproduce the above copyright 
00012      notice, this list of conditions and the following disclaimer in 
00013      the documentation and/or other materials provided with the distribution.
00014 
00015   3. The names of the authors may not be used to endorse or promote products
00016      derived from this software without specific prior written permission.
00017 
00018 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
00019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
00020 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
00021 INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
00022 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00023 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00024 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00025 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00026 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
00027 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028 */
00029 
00030 package com.jcraft.jsch;
00031 
00032 import java.io.*;
00033 
00034 
00045 public
00046 class KnownHosts implements HostKeyRepository{
00047   private static final String _known_hosts="known_hosts";
00048 
00049   /*
00050   static final int SSHDSS=0;
00051   static final int SSHRSA=1;
00052   static final int UNKNOWN=2;
00053   */
00054 
00055   private JSch jsch=null;
00056   private String known_hosts=null;
00057   private java.util.Vector pool=null;
00058 
00059   private MAC hmacsha1=null;
00060 
00061   KnownHosts(JSch jsch){
00062     super();
00063     this.jsch=jsch;
00064     pool=new java.util.Vector();
00065   }
00066 
00067   void setKnownHosts(String foo) throws JSchException{
00068     try{
00069       known_hosts=foo;
00070       FileInputStream fis=new FileInputStream(foo);
00071       setKnownHosts(fis);
00072     }
00073     catch(FileNotFoundException e){
00074     } 
00075   }
00076   void setKnownHosts(InputStream foo) throws JSchException{
00077     pool.removeAllElements();
00078     StringBuffer sb=new StringBuffer();
00079     byte i;
00080     int j;
00081     boolean error=false;
00082     try{
00083       InputStream fis=foo;
00084       String host;
00085       String key=null;
00086       int type;
00087       byte[] buf=new byte[1024];
00088       int bufl=0;
00089 loop:
00090       while(true){
00091     bufl=0;
00092         while(true){
00093           j=fis.read();
00094           if(j==-1){
00095             if(bufl==0){ break loop; }
00096             else{ break; }
00097           }
00098       if(j==0x0d){ continue; }
00099       if(j==0x0a){ break; }
00100           if(buf.length<=bufl){
00101             if(bufl>1024*10) break;   // too long...
00102             byte[] newbuf=new byte[buf.length*2];
00103             System.arraycopy(buf, 0, newbuf, 0, buf.length);
00104             buf=newbuf;
00105           }
00106           buf[bufl++]=(byte)j;
00107     }
00108 
00109     j=0;
00110         while(j<bufl){
00111           i=buf[j];
00112       if(i==' '||i=='\t'){ j++; continue; }
00113       if(i=='#'){
00114         addInvalidLine(Util.byte2str(buf, 0, bufl));
00115         continue loop;
00116       }
00117       break;
00118     }
00119     if(j>=bufl){ 
00120       addInvalidLine(Util.byte2str(buf, 0, bufl));
00121       continue loop; 
00122     }
00123 
00124         sb.setLength(0);
00125         while(j<bufl){
00126           i=buf[j++];
00127           if(i==0x20 || i=='\t'){ break; }
00128           sb.append((char)i);
00129     }
00130     host=sb.toString();
00131     if(j>=bufl || host.length()==0){
00132       addInvalidLine(Util.byte2str(buf, 0, bufl));
00133       continue loop; 
00134     }
00135 
00136         sb.setLength(0);
00137     type=-1;
00138         while(j<bufl){
00139           i=buf[j++];
00140           if(i==0x20 || i=='\t'){ break; }
00141           sb.append((char)i);
00142     }
00143     if(sb.toString().equals("ssh-dss")){ type=HostKey.SSHDSS; }
00144     else if(sb.toString().equals("ssh-rsa")){ type=HostKey.SSHRSA; }
00145     else { j=bufl; }
00146     if(j>=bufl){
00147       addInvalidLine(Util.byte2str(buf, 0, bufl));
00148       continue loop; 
00149     }
00150 
00151         sb.setLength(0);
00152         while(j<bufl){
00153           i=buf[j++];
00154           if(i==0x0d){ continue; }
00155           if(i==0x0a){ break; }
00156           sb.append((char)i);
00157     }
00158     key=sb.toString();
00159     if(key.length()==0){
00160       addInvalidLine(Util.byte2str(buf, 0, bufl));
00161       continue loop; 
00162     }
00163 
00164     //System.err.println(host);
00165     //System.err.println("|"+key+"|");
00166 
00167     HostKey hk = null;
00168         hk = new HashedHostKey(host, type, 
00169                                Util.fromBase64(Util.str2byte(key), 0, 
00170                                                key.length()));
00171     pool.addElement(hk);
00172       }
00173       fis.close();
00174       if(error){
00175     throw new JSchException("KnownHosts: invalid format");
00176       }
00177     }
00178     catch(Exception e){
00179       if(e instanceof JSchException)
00180     throw (JSchException)e;         
00181       if(e instanceof Throwable)
00182         throw new JSchException(e.toString(), (Throwable)e);
00183       throw new JSchException(e.toString());
00184     }
00185   }
00186   private void addInvalidLine(String line) throws JSchException {
00187     HostKey hk = new HostKey(line, HostKey.UNKNOWN, null);
00188     pool.addElement(hk);
00189   }
00190   String getKnownHostsFile(){ return known_hosts; }
00191   public String getKnownHostsRepositoryID(){ return known_hosts; }
00192 
00193   public int check(String host, byte[] key){
00194     int result=NOT_INCLUDED;
00195     if(host==null){
00196       return result;
00197     }
00198 
00199     int type=getType(key);
00200     HostKey hk;
00201 
00202     synchronized(pool){
00203       for(int i=0; i<pool.size(); i++){
00204         hk=(HostKey)(pool.elementAt(i));
00205         if(hk.isMatched(host) && hk.type==type){
00206           if(Util.array_equals(hk.key, key)){
00207             return OK;
00208           }
00209           else{
00210             result=CHANGED;
00211       }
00212         }
00213       }
00214     }
00215 
00216     if(result==NOT_INCLUDED &&
00217        host.startsWith("[") &&
00218        host.indexOf("]:")>1
00219        ){
00220       return check(host.substring(1, host.indexOf("]:")), key);
00221     }
00222 
00223     return result;
00224   }
00225   public void add(HostKey hostkey, UserInfo userinfo){
00226     int type=hostkey.type;
00227     String host=hostkey.getHost();
00228     byte[] key=hostkey.key;
00229 
00230     HostKey hk=null;
00231     synchronized(pool){
00232       for(int i=0; i<pool.size(); i++){
00233         hk=(HostKey)(pool.elementAt(i));
00234         if(hk.isMatched(host) && hk.type==type){
00235 /*
00236       if(Util.array_equals(hk.key, key)){ return; }
00237       if(hk.host.equals(host)){
00238         hk.key=key;
00239         return;
00240       }
00241       else{
00242         hk.host=deleteSubString(hk.host, host);
00243         break;
00244       }
00245 */
00246         }
00247       }
00248     }
00249 
00250     hk=hostkey;
00251 
00252     pool.addElement(hk);
00253 
00254     String bar=getKnownHostsRepositoryID();
00255     if(bar!=null){
00256       boolean foo=true;
00257       File goo=new File(bar);
00258       if(!goo.exists()){
00259         foo=false;
00260         if(userinfo!=null){
00261           foo=userinfo.promptYesNo(bar+" does not exist.\n"+
00262                                    "Are you sure you want to create it?"
00263                                    );
00264           goo=goo.getParentFile();
00265           if(foo && goo!=null && !goo.exists()){
00266             foo=userinfo.promptYesNo("The parent directory "+goo+" does not exist.\n"+
00267                                      "Are you sure you want to create it?"
00268                                      );
00269             if(foo){
00270               if(!goo.mkdirs()){
00271                 userinfo.showMessage(goo+" has not been created.");
00272                 foo=false;
00273               }
00274               else{
00275                 userinfo.showMessage(goo+" has been succesfully created.\nPlease check its access permission.");
00276               }
00277             }
00278           }
00279           if(goo==null)foo=false;
00280         }
00281       }
00282       if(foo){
00283         try{ 
00284           sync(bar); 
00285         }
00286         catch(Exception e){ System.err.println("sync known_hosts: "+e); }
00287       }
00288     }
00289   }
00290 
00291   public HostKey[] getHostKey(){
00292     return getHostKey(null, null);
00293   }
00294   public HostKey[] getHostKey(String host, String type){
00295     synchronized(pool){
00296       int count=0;
00297       for(int i=0; i<pool.size(); i++){
00298     HostKey hk=(HostKey)pool.elementAt(i);
00299     if(hk.type==HostKey.UNKNOWN) continue;
00300     if(host==null || 
00301        (hk.isMatched(host) && 
00302         (type==null || hk.getType().equals(type)))){
00303       count++;
00304     }
00305       }
00306       if(count==0)return null;
00307       HostKey[] foo=new HostKey[count];
00308       int j=0;
00309       for(int i=0; i<pool.size(); i++){
00310     HostKey hk=(HostKey)pool.elementAt(i);
00311     if(hk.type==HostKey.UNKNOWN) continue;
00312     if(host==null || 
00313        (hk.isMatched(host) && 
00314         (type==null || hk.getType().equals(type)))){
00315       foo[j++]=hk;
00316     }
00317       }
00318       return foo;
00319     }
00320   }
00321   public void remove(String host, String type){
00322     remove(host, type, null);
00323   }
00324   public void remove(String host, String type, byte[] key){
00325     boolean sync=false;
00326     synchronized(pool){
00327     for(int i=0; i<pool.size(); i++){
00328       HostKey hk=(HostKey)(pool.elementAt(i));
00329       if(host==null ||
00330      (hk.isMatched(host) && 
00331       (type==null || (hk.getType().equals(type) &&
00332               (key==null || Util.array_equals(key, hk.key)))))){
00333         String hosts=hk.getHost();
00334         if(hosts.equals(host) || 
00335            ((hk instanceof HashedHostKey) &&
00336             ((HashedHostKey)hk).isHashed())){
00337           pool.removeElement(hk);
00338         }
00339         else{
00340           hk.host=deleteSubString(hosts, host);
00341         }
00342     sync=true;
00343       }
00344     }
00345     }
00346     if(sync){
00347       try{sync();}catch(Exception e){};
00348     }
00349   }
00350 
00351   protected void sync() throws IOException { 
00352     if(known_hosts!=null)
00353       sync(known_hosts); 
00354   }
00355   protected synchronized void sync(String foo) throws IOException {
00356     if(foo==null) return;
00357     FileOutputStream fos=new FileOutputStream(foo);
00358     dump(fos);
00359     fos.close();
00360   }
00361 
00362   private static final byte[] space={(byte)0x20};
00363   private static final byte[] cr=Util.str2byte("\n");
00364   void dump(OutputStream out) throws IOException {
00365     try{
00366       HostKey hk;
00367       synchronized(pool){
00368       for(int i=0; i<pool.size(); i++){
00369         hk=(HostKey)(pool.elementAt(i));
00370         //hk.dump(out);
00371     String host=hk.getHost();
00372     String type=hk.getType();
00373     if(type.equals("UNKNOWN")){
00374       out.write(Util.str2byte(host));
00375       out.write(cr);
00376       continue;
00377     }
00378     out.write(Util.str2byte(host));
00379     out.write(space);
00380     out.write(Util.str2byte(type));
00381     out.write(space);
00382     out.write(Util.str2byte(hk.getKey()));
00383     out.write(cr);
00384       }
00385       }
00386     }
00387     catch(Exception e){
00388       System.err.println(e);
00389     }
00390   }
00391   private int getType(byte[] key){
00392     if(key[8]=='d') return HostKey.SSHDSS;
00393     if(key[8]=='r') return HostKey.SSHRSA;
00394     return HostKey.UNKNOWN;
00395   }
00396   private String deleteSubString(String hosts, String host){
00397     int i=0;
00398     int hostlen=host.length();
00399     int hostslen=hosts.length();
00400     int j;
00401     while(i<hostslen){
00402       j=hosts.indexOf(',', i);
00403       if(j==-1) break;
00404       if(!host.equals(hosts.substring(i, j))){
00405         i=j+1;    
00406         continue;
00407       }
00408       return hosts.substring(0, i)+hosts.substring(j+1);
00409     }
00410     if(hosts.endsWith(host) && hostslen-i==hostlen){
00411       return hosts.substring(0, (hostlen==hostslen) ? 0 :hostslen-hostlen-1);
00412     }
00413     return hosts;
00414   }
00415 
00416   private synchronized MAC getHMACSHA1(){
00417     if(hmacsha1==null){
00418       try{
00419         Class c=Class.forName(jsch.getConfig("hmac-sha1"));
00420         hmacsha1=(MAC)(c.newInstance());
00421       }
00422       catch(Exception e){ 
00423         System.err.println("hmacsha1: "+e); 
00424       }
00425     }
00426     return hmacsha1;
00427   }
00428 
00429   HostKey createHashedHostKey(String host, byte[]key) throws JSchException {
00430     HashedHostKey hhk=new HashedHostKey(host, key);
00431     hhk.hash();
00432     return hhk;
00433   } 
00434   class HashedHostKey extends HostKey{
00435     private static final String HASH_MAGIC="|1|";
00436     private static final String HASH_DELIM="|";
00437 
00438     private boolean hashed=false;
00439     byte[] salt=null;
00440     byte[] hash=null;
00441 
00442 
00443     HashedHostKey(String host, byte[] key) throws JSchException {
00444       this(host, GUESS, key);
00445     }
00446     HashedHostKey(String host, int type, byte[] key) throws JSchException {
00447       super(host, type, key);
00448       if(this.host.startsWith(HASH_MAGIC) &&
00449          this.host.substring(HASH_MAGIC.length()).indexOf(HASH_DELIM)>0){
00450         String data=this.host.substring(HASH_MAGIC.length());
00451         String _salt=data.substring(0, data.indexOf(HASH_DELIM));
00452         String _hash=data.substring(data.indexOf(HASH_DELIM)+1);
00453         salt=Util.fromBase64(Util.str2byte(_salt), 0, _salt.length());
00454         hash=Util.fromBase64(Util.str2byte(_hash), 0, _hash.length());
00455         if(salt.length!=20 ||  // block size of hmac-sha1
00456            hash.length!=20){
00457           salt=null;
00458           hash=null;
00459           return;
00460         }
00461         hashed=true;
00462       }
00463     }
00464 
00465     boolean isMatched(String _host){
00466       if(!hashed){
00467         return super.isMatched(_host);
00468       }
00469       MAC macsha1=getHMACSHA1();
00470       try{
00471         synchronized(macsha1){
00472           macsha1.init(salt);
00473           byte[] foo=Util.str2byte(_host);
00474           macsha1.update(foo, 0, foo.length);
00475           byte[] bar=new byte[macsha1.getBlockSize()];
00476           macsha1.doFinal(bar, 0);
00477           return Util.array_equals(hash, bar);
00478         }
00479       }
00480       catch(Exception e){
00481         System.out.println(e);
00482       }
00483       return false;
00484     }
00485 
00486     boolean isHashed(){
00487       return hashed;
00488     }
00489 
00490     void hash(){
00491       if(hashed)
00492         return;
00493       MAC macsha1=getHMACSHA1();
00494       if(salt==null){
00495         Random random=Session.random;
00496         synchronized(random){
00497           salt=new byte[macsha1.getBlockSize()];
00498           random.fill(salt, 0, salt.length);
00499         }
00500       }
00501       try{
00502         synchronized(macsha1){
00503           macsha1.init(salt);
00504           byte[] foo=Util.str2byte(host);
00505           macsha1.update(foo, 0, foo.length);
00506           hash=new byte[macsha1.getBlockSize()];
00507           macsha1.doFinal(hash, 0);
00508         }
00509       }
00510       catch(Exception e){
00511       }
00512       host=HASH_MAGIC+Util.byte2str(Util.toBase64(salt, 0, salt.length))+
00513         HASH_DELIM+Util.byte2str(Util.toBase64(hash, 0, hash.length));
00514       hashed=true;
00515     }
00516   }
00517 }

Generated on 5 May 2015 for HPCVIEWER by  doxygen 1.6.1