00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
00051
00052
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;
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
00165
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
00237
00238
00239
00240
00241
00242
00243
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
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 ||
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 }