]> Joshua Wise's Git repositories - dumload.git/blame - src/com/jcraft/jsch/KnownHosts.java
Initial commit.
[dumload.git] / src / com / jcraft / jsch / KnownHosts.java
CommitLineData
0763e16d
JW
1/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
2/*
3Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
18THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
21INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
22INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30package com.jcraft.jsch;
31
32import java.io.*;
33
34public
35class 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;
78loop:
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.062359 seconds and 4 git commands to generate.