]> Joshua Wise's Git repositories - dumload.git/blob - src/com/jcraft/jsch/KnownHosts.java
Initial commit.
[dumload.git] / src / com / jcraft / jsch / KnownHosts.java
1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
2 /*
3 Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8   1. Redistributions of source code must retain the above copyright notice,
9      this list of conditions and the following disclaimer.
10
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.
14
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.
17
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.
28 */
29
30 package com.jcraft.jsch;
31
32 import java.io.*;
33
34 public
35 class KnownHosts implements HostKeyRepository{
36   private static final String _known_hosts="known_hosts";
37
38   /*
39   static final int SSHDSS=0;
40   static final int SSHRSA=1;
41   static final int UNKNOWN=2;
42   */
43
44   private JSch jsch=null;
45   private String known_hosts=null;
46   private java.util.Vector pool=null;
47
48   private MAC hmacsha1=null;
49
50   KnownHosts(JSch jsch){
51     super();
52     this.jsch=jsch;
53     pool=new java.util.Vector();
54   }
55
56   void setKnownHosts(String foo) throws JSchException{
57     try{
58       known_hosts=foo;
59       FileInputStream fis=new FileInputStream(foo);
60       setKnownHosts(fis);
61     }
62     catch(FileNotFoundException e){
63     } 
64   }
65   void setKnownHosts(InputStream foo) throws JSchException{
66     pool.removeAllElements();
67     StringBuffer sb=new StringBuffer();
68     byte i;
69     int j;
70     boolean error=false;
71     try{
72       InputStream fis=foo;
73       String host;
74       String key=null;
75       int type;
76       byte[] buf=new byte[1024];
77       int bufl=0;
78 loop:
79       while(true){
80         bufl=0;
81         while(true){
82           j=fis.read();
83           if(j==-1){
84             if(bufl==0){ break loop; }
85             else{ break; }
86           }
87           if(j==0x0d){ continue; }
88           if(j==0x0a){ break; }
89           if(buf.length<=bufl){
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);
93             buf=newbuf;
94           }
95           buf[bufl++]=(byte)j;
96         }
97
98         j=0;
99         while(j<bufl){
100           i=buf[j];
101           if(i==' '||i=='\t'){ j++; continue; }
102           if(i=='#'){
103             addInvalidLine(Util.byte2str(buf, 0, bufl));
104             continue loop;
105           }
106           break;
107         }
108         if(j>=bufl){ 
109           addInvalidLine(Util.byte2str(buf, 0, bufl));
110           continue loop; 
111         }
112
113         sb.setLength(0);
114         while(j<bufl){
115           i=buf[j++];
116           if(i==0x20 || i=='\t'){ break; }
117           sb.append((char)i);
118         }
119         host=sb.toString();
120         if(j>=bufl || host.length()==0){
121           addInvalidLine(Util.byte2str(buf, 0, bufl));
122           continue loop; 
123         }
124
125         sb.setLength(0);
126         type=-1;
127         while(j<bufl){
128           i=buf[j++];
129           if(i==0x20 || i=='\t'){ break; }
130           sb.append((char)i);
131         }
132         if(sb.toString().equals("ssh-dss")){ type=HostKey.SSHDSS; }
133         else if(sb.toString().equals("ssh-rsa")){ type=HostKey.SSHRSA; }
134         else { j=bufl; }
135         if(j>=bufl){
136           addInvalidLine(Util.byte2str(buf, 0, bufl));
137           continue loop; 
138         }
139
140         sb.setLength(0);
141         while(j<bufl){
142           i=buf[j++];
143           if(i==0x0d){ continue; }
144           if(i==0x0a){ break; }
145           sb.append((char)i);
146         }
147         key=sb.toString();
148         if(key.length()==0){
149           addInvalidLine(Util.byte2str(buf, 0, bufl));
150           continue loop; 
151         }
152
153         //System.err.println(host);
154         //System.err.println("|"+key+"|");
155
156         HostKey hk = null;
157         hk = new HashedHostKey(host, type, 
158                                Util.fromBase64(Util.str2byte(key), 0, 
159                                                key.length()));
160         pool.addElement(hk);
161       }
162       fis.close();
163       if(error){
164         throw new JSchException("KnownHosts: invalid format");
165       }
166     }
167     catch(Exception e){
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());
173     }
174   }
175   private void addInvalidLine(String line) throws JSchException {
176     HostKey hk = new HostKey(line, HostKey.UNKNOWN, null);
177     pool.addElement(hk);
178   }
179   String getKnownHostsFile(){ return known_hosts; }
180   public String getKnownHostsRepositoryID(){ return known_hosts; }
181
182   public int check(String host, byte[] key){
183     int result=NOT_INCLUDED;
184     if(host==null){
185       return result;
186     }
187
188     int type=getType(key);
189     HostKey hk;
190
191     synchronized(pool){
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)){
196             return OK;
197           }
198           else{
199             result=CHANGED;
200           }
201         }
202       }
203     }
204
205     if(result==NOT_INCLUDED &&
206        host.startsWith("[") &&
207        host.indexOf("]:")>1
208        ){
209       return check(host.substring(1, host.indexOf("]:")), key);
210     }
211
212     return result;
213   }
214   public void add(HostKey hostkey, UserInfo userinfo){
215     int type=hostkey.type;
216     String host=hostkey.getHost();
217     byte[] key=hostkey.key;
218
219     HostKey hk=null;
220     synchronized(pool){
221       for(int i=0; i<pool.size(); i++){
222         hk=(HostKey)(pool.elementAt(i));
223         if(hk.isMatched(host) && hk.type==type){
224 /*
225           if(Util.array_equals(hk.key, key)){ return; }
226           if(hk.host.equals(host)){
227             hk.key=key;
228             return;
229           }
230           else{
231             hk.host=deleteSubString(hk.host, host);
232             break;
233           }
234 */
235         }
236       }
237     }
238
239     hk=hostkey;
240
241     pool.addElement(hk);
242
243     String bar=getKnownHostsRepositoryID();
244     if(bar!=null){
245       boolean foo=true;
246       File goo=new File(bar);
247       if(!goo.exists()){
248         foo=false;
249         if(userinfo!=null){
250           foo=userinfo.promptYesNo(bar+" does not exist.\n"+
251                                    "Are you sure you want to create it?"
252                                    );
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?"
257                                      );
258             if(foo){
259               if(!goo.mkdirs()){
260                 userinfo.showMessage(goo+" has not been created.");
261                 foo=false;
262               }
263               else{
264                 userinfo.showMessage(goo+" has been succesfully created.\nPlease check its access permission.");
265               }
266             }
267           }
268           if(goo==null)foo=false;
269         }
270       }
271       if(foo){
272         try{ 
273           sync(bar); 
274         }
275         catch(Exception e){ System.err.println("sync known_hosts: "+e); }
276       }
277     }
278   }
279
280   public HostKey[] getHostKey(){
281     return getHostKey(null, null);
282   }
283   public HostKey[] getHostKey(String host, String type){
284     synchronized(pool){
285       int count=0;
286       for(int i=0; i<pool.size(); i++){
287         HostKey hk=(HostKey)pool.elementAt(i);
288         if(hk.type==HostKey.UNKNOWN) continue;
289         if(host==null || 
290            (hk.isMatched(host) && 
291             (type==null || hk.getType().equals(type)))){
292           count++;
293         }
294       }
295       if(count==0)return null;
296       HostKey[] foo=new HostKey[count];
297       int j=0;
298       for(int i=0; i<pool.size(); i++){
299         HostKey hk=(HostKey)pool.elementAt(i);
300         if(hk.type==HostKey.UNKNOWN) continue;
301         if(host==null || 
302            (hk.isMatched(host) && 
303             (type==null || hk.getType().equals(type)))){
304           foo[j++]=hk;
305         }
306       }
307       return foo;
308     }
309   }
310   public void remove(String host, String type){
311     remove(host, type, null);
312   }
313   public void remove(String host, String type, byte[] key){
314     boolean sync=false;
315     synchronized(pool){
316     for(int i=0; i<pool.size(); i++){
317       HostKey hk=(HostKey)(pool.elementAt(i));
318       if(host==null ||
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);
327         }
328         else{
329           hk.host=deleteSubString(hosts, host);
330         }
331         sync=true;
332       }
333     }
334     }
335     if(sync){
336       try{sync();}catch(Exception e){};
337     }
338   }
339
340   protected void sync() throws IOException { 
341     if(known_hosts!=null)
342       sync(known_hosts); 
343   }
344   protected synchronized void sync(String foo) throws IOException {
345     if(foo==null) return;
346     FileOutputStream fos=new FileOutputStream(foo);
347     dump(fos);
348     fos.close();
349   }
350
351   private static final byte[] space={(byte)0x20};
352   private static final byte[] cr=Util.str2byte("\n");
353   void dump(OutputStream out) throws IOException {
354     try{
355       HostKey hk;
356       synchronized(pool){
357       for(int i=0; i<pool.size(); i++){
358         hk=(HostKey)(pool.elementAt(i));
359         //hk.dump(out);
360         String host=hk.getHost();
361         String type=hk.getType();
362         if(type.equals("UNKNOWN")){
363           out.write(Util.str2byte(host));
364           out.write(cr);
365           continue;
366         }
367         out.write(Util.str2byte(host));
368         out.write(space);
369         out.write(Util.str2byte(type));
370         out.write(space);
371         out.write(Util.str2byte(hk.getKey()));
372         out.write(cr);
373       }
374       }
375     }
376     catch(Exception e){
377       System.err.println(e);
378     }
379   }
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;
384   }
385   private String deleteSubString(String hosts, String host){
386     int i=0;
387     int hostlen=host.length();
388     int hostslen=hosts.length();
389     int j;
390     while(i<hostslen){
391       j=hosts.indexOf(',', i);
392       if(j==-1) break;
393       if(!host.equals(hosts.substring(i, j))){
394         i=j+1;    
395         continue;
396       }
397       return hosts.substring(0, i)+hosts.substring(j+1);
398     }
399     if(hosts.endsWith(host) && hostslen-i==hostlen){
400       return hosts.substring(0, (hostlen==hostslen) ? 0 :hostslen-hostlen-1);
401     }
402     return hosts;
403   }
404
405   private synchronized MAC getHMACSHA1(){
406     if(hmacsha1==null){
407       try{
408         Class c=Class.forName(jsch.getConfig("hmac-sha1"));
409         hmacsha1=(MAC)(c.newInstance());
410       }
411       catch(Exception e){ 
412         System.err.println("hmacsha1: "+e); 
413       }
414     }
415     return hmacsha1;
416   }
417
418   HostKey createHashedHostKey(String host, byte[]key) throws JSchException {
419     HashedHostKey hhk=new HashedHostKey(host, key);
420     hhk.hash();
421     return hhk;
422   } 
423   class HashedHostKey extends HostKey{
424     private static final String HASH_MAGIC="|1|";
425     private static final String HASH_DELIM="|";
426
427     private boolean hashed=false;
428     byte[] salt=null;
429     byte[] hash=null;
430
431
432     HashedHostKey(String host, byte[] key) throws JSchException {
433       this(host, GUESS, key);
434     }
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
445            hash.length!=20){
446           salt=null;
447           hash=null;
448           return;
449         }
450         hashed=true;
451       }
452     }
453
454     boolean isMatched(String _host){
455       if(!hashed){
456         return super.isMatched(_host);
457       }
458       MAC macsha1=getHMACSHA1();
459       try{
460         synchronized(macsha1){
461           macsha1.init(salt);
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);
467         }
468       }
469       catch(Exception e){
470         System.out.println(e);
471       }
472       return false;
473     }
474
475     boolean isHashed(){
476       return hashed;
477     }
478
479     void hash(){
480       if(hashed)
481         return;
482       MAC macsha1=getHMACSHA1();
483       if(salt==null){
484         Random random=Session.random;
485         synchronized(random){
486           salt=new byte[macsha1.getBlockSize()];
487           random.fill(salt, 0, salt.length);
488         }
489       }
490       try{
491         synchronized(macsha1){
492           macsha1.init(salt);
493           byte[] foo=Util.str2byte(host);
494           macsha1.update(foo, 0, foo.length);
495           hash=new byte[macsha1.getBlockSize()];
496           macsha1.doFinal(hash, 0);
497         }
498       }
499       catch(Exception e){
500       }
501       host=HASH_MAGIC+Util.byte2str(Util.toBase64(salt, 0, salt.length))+
502         HASH_DELIM+Util.byte2str(Util.toBase64(hash, 0, hash.length));
503       hashed=true;
504     }
505   }
506 }
This page took 0.051357 seconds and 4 git commands to generate.