Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /trunk/ttssh2/ttxssh/hosts.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2876 - (hide annotations) (download) (as text)
Thu Jun 29 15:27:00 2006 UTC (17 years, 9 months ago) by yutakakn
Original Path: ttssh2/trunk/ttxssh/hosts.c
File MIME type: text/x-csrc
File size: 33633 byte(s)
ssh_known_filesファイルを常にTeraTermインストールディレクトリへ保存するようにした。

1 yutakakn 2728 /*
2     Copyright (c) 1998-2001, Robert O'Callahan
3     All rights reserved.
4    
5     Redistribution and use in source and binary forms, with or without modification,
6     are permitted provided that the following conditions are met:
7    
8     Redistributions of source code must retain the above copyright notice, this list of
9     conditions and the following disclaimer.
10    
11     Redistributions in binary form must reproduce the above copyright notice, this list
12     of conditions and the following disclaimer in the documentation and/or other materials
13     provided with the distribution.
14    
15     The name of Robert O'Callahan may not be used to endorse or promote products derived from
16     this software without specific prior written permission.
17    
18     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
19     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21     THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27     */
28    
29     /*
30     This code is copyright (C) 1998-1999 Robert O'Callahan.
31     See LICENSE.TXT for the license.
32     */
33    
34     #include "ttxssh.h"
35     #include "util.h"
36     #include "resource.h"
37     #include "matcher.h"
38 yutakakn 2856 #include "ssh.h"
39     #include "hosts.h"
40 yutakakn 2728
41     #include <openssl/bn.h>
42 yutakakn 2856 #include <openssl/evp.h>
43     #include <openssl/rsa.h>
44     #include <openssl/dsa.h>
45 yutakakn 2728
46     #include <fcntl.h>
47     #include <io.h>
48     #include <errno.h>
49     #include <sys/stat.h>
50    
51 yutakakn 2856
52     // BASE64�\���������i��������'='�����������������j
53     static char base64[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54    
55    
56     // �z�X�g�L�[�������� (2006.3.21 yutaka)
57     static void init_hostkey(Key *key)
58     {
59     key->type = KEY_UNSPEC;
60    
61     // SSH1
62     key->bits = 0;
63     if (key->exp != NULL) {
64     free(key->exp);
65     key->exp = NULL;
66     }
67     if (key->mod != NULL) {
68     free(key->mod);
69     key->mod = NULL;
70     }
71    
72     // SSH2
73     if (key->dsa != NULL) {
74     DSA_free(key->dsa);
75     key->dsa = NULL;
76     }
77     if (key->rsa != NULL) {
78     RSA_free(key->rsa);
79     key->rsa = NULL;
80     }
81     }
82    
83    
84 yutakakn 2728 static char FAR *FAR * parse_multi_path(char FAR * buf)
85     {
86     int i;
87     int ch;
88     int num_paths = 1;
89     char FAR *FAR * result;
90     int last_path_index;
91    
92     for (i = 0; (ch = buf[i]) != 0; i++) {
93     if (ch == ';') {
94     num_paths++;
95     }
96     }
97    
98     result =
99     (char FAR * FAR *) malloc(sizeof(char FAR *) * (num_paths + 1));
100    
101     last_path_index = 0;
102     num_paths = 0;
103     for (i = 0; (ch = buf[i]) != 0; i++) {
104     if (ch == ';') {
105     buf[i] = 0;
106     result[num_paths] = _strdup(buf + last_path_index);
107     num_paths++;
108     buf[i] = ch;
109     last_path_index = i + 1;
110     }
111     }
112     if (i > last_path_index) {
113     result[num_paths] = _strdup(buf + last_path_index);
114     num_paths++;
115     }
116     result[num_paths] = NULL;
117     return result;
118     }
119    
120     void HOSTS_init(PTInstVar pvar)
121     {
122     pvar->hosts_state.prefetched_hostname = NULL;
123 yutakakn 2856 #if 0
124 yutakakn 2728 pvar->hosts_state.key_exp = NULL;
125     pvar->hosts_state.key_mod = NULL;
126 yutakakn 2856 #else
127     init_hostkey(&pvar->hosts_state.hostkey);
128     #endif
129 yutakakn 2728 pvar->hosts_state.hosts_dialog = NULL;
130     pvar->hosts_state.file_names = NULL;
131     }
132    
133     void HOSTS_open(PTInstVar pvar)
134     {
135     pvar->hosts_state.file_names =
136     parse_multi_path(pvar->session_settings.KnownHostsFiles);
137     }
138    
139 yutakakn 2856 //
140     // known_hosts�t�@�C�������e�������� pvar->hosts_state.file_data ����������
141     //
142 yutakakn 2728 static int begin_read_file(PTInstVar pvar, char FAR * name,
143     int suppress_errors)
144     {
145     int fd;
146     int length;
147     int amount_read;
148     char buf[2048];
149    
150     get_teraterm_dir_relative_name(buf, sizeof(buf), name);
151     fd = _open(buf, _O_RDONLY | _O_SEQUENTIAL | _O_BINARY);
152     if (fd == -1) {
153     if (!suppress_errors) {
154     if (errno == ENOENT) {
155     notify_nonfatal_error(pvar,
156     "An error occurred while trying to read a known_hosts file.\n"
157     "The specified filename does not exist.");
158     } else {
159     notify_nonfatal_error(pvar,
160     "An error occurred while trying to read a known_hosts file.");
161     }
162     }
163     return 0;
164     }
165    
166     length = (int) _lseek(fd, 0, SEEK_END);
167     _lseek(fd, 0, SEEK_SET);
168    
169     if (length >= 0 && length < 0x7FFFFFFF) {
170     pvar->hosts_state.file_data = malloc(length + 1);
171     if (pvar->hosts_state.file_data == NULL) {
172     if (!suppress_errors) {
173     notify_nonfatal_error(pvar,
174     "Memory ran out while trying to allocate space to read a known_hosts file.");
175     }
176     _close(fd);
177     return 0;
178     }
179     } else {
180     if (!suppress_errors) {
181     notify_nonfatal_error(pvar,
182     "An error occurred while trying to read a known_hosts file.");
183     }
184     _close(fd);
185     return 0;
186     }
187    
188     amount_read = _read(fd, pvar->hosts_state.file_data, length);
189     pvar->hosts_state.file_data[length] = 0;
190    
191     _close(fd);
192    
193     if (amount_read != length) {
194     if (!suppress_errors) {
195     notify_nonfatal_error(pvar,
196     "An error occurred while trying to read a known_hosts file.");
197     }
198     free(pvar->hosts_state.file_data);
199     pvar->hosts_state.file_data = NULL;
200     return 0;
201     } else {
202     return 1;
203     }
204     }
205    
206     static int end_read_file(PTInstVar pvar, int suppress_errors)
207     {
208     free(pvar->hosts_state.file_data);
209     pvar->hosts_state.file_data = NULL;
210     return 1;
211     }
212    
213     static int begin_read_host_files(PTInstVar pvar, int suppress_errors)
214     {
215     pvar->hosts_state.file_num = 0;
216     pvar->hosts_state.file_data = NULL;
217     return 1;
218     }
219    
220 yutakakn 2856 // MIME64�����������X�L�b�v����
221     static int eat_base64(char FAR * data)
222     {
223     int index = 0;
224     int ch;
225    
226     for (;;) {
227     ch = data[index];
228     if (ch == '=' || strchr(base64, ch)) {
229     // BASE64���\�������������������� index ���i����
230     index++;
231     } else {
232     break;
233     }
234     }
235    
236     return index;
237     }
238    
239 yutakakn 2728 static int eat_spaces(char FAR * data)
240     {
241     int index = 0;
242     int ch;
243    
244     while ((ch = data[index]) == ' ' || ch == '\t') {
245     index++;
246     }
247     return index;
248     }
249    
250     static int eat_digits(char FAR * data)
251     {
252     int index = 0;
253     int ch;
254    
255     while ((ch = data[index]) >= '0' && ch <= '9') {
256     index++;
257     }
258     return index;
259     }
260    
261     static int eat_to_end_of_line(char FAR * data)
262     {
263     int index = 0;
264     int ch;
265    
266     while ((ch = data[index]) != '\n' && ch != '\r' && ch != 0) {
267     index++;
268     }
269    
270     while ((ch = data[index]) == '\n' || ch == '\r') {
271     index++;
272     }
273    
274     return index;
275     }
276    
277     static int eat_to_end_of_pattern(char FAR * data)
278     {
279     int index = 0;
280     int ch;
281    
282     while (ch = data[index], is_pattern_char(ch)) {
283     index++;
284     }
285    
286     return index;
287     }
288    
289 yutakakn 2856 //
290     // BASE64�f�R�[�h�������s���B(rfc1521)
291     // src�o�b�t�@�� null-terminate ���������K�v�����B
292     //
293     static int uudecode(unsigned char *src, int srclen, unsigned char *target, int targsize)
294     {
295     char pad = '=';
296     int tarindex, state, ch;
297     char *pos;
298    
299     state = 0;
300     tarindex = 0;
301    
302     while ((ch = *src++) != '\0') {
303     if (isspace(ch)) /* Skip whitespace anywhere. */
304     continue;
305    
306     if (ch == pad)
307     break;
308    
309     pos = strchr(base64, ch);
310     if (pos == 0) /* A non-base64 character. */
311     return (-1);
312    
313     switch (state) {
314     case 0:
315     if (target) {
316     if (tarindex >= targsize)
317     return (-1);
318     target[tarindex] = (pos - base64) << 2;
319     }
320     state = 1;
321     break;
322     case 1:
323     if (target) {
324     if (tarindex + 1 >= targsize)
325     return (-1);
326     target[tarindex] |= (pos - base64) >> 4;
327     target[tarindex+1] = ((pos - base64) & 0x0f) << 4 ;
328     }
329     tarindex++;
330     state = 2;
331     break;
332     case 2:
333     if (target) {
334     if (tarindex + 1 >= targsize)
335     return (-1);
336     target[tarindex] |= (pos - base64) >> 2;
337     target[tarindex+1] = ((pos - base64) & 0x03) << 6;
338     }
339     tarindex++;
340     state = 3;
341     break;
342     case 3:
343     if (target) {
344     if (tarindex >= targsize)
345     return (-1);
346     target[tarindex] |= (pos - base64);
347     }
348     tarindex++;
349     state = 0;
350     break;
351     }
352     }
353    
354     /*
355     * We are done decoding Base-64 chars. Let's see if we ended
356     * on a byte boundary, and/or with erroneous trailing characters.
357     */
358    
359     if (ch == pad) { /* We got a pad char. */
360     ch = *src++; /* Skip it, get next. */
361     switch (state) {
362     case 0: /* Invalid = in first position */
363     case 1: /* Invalid = in second position */
364     return (-1);
365    
366     case 2: /* Valid, means one byte of info */
367     /* Skip any number of spaces. */
368     for (; ch != '\0'; ch = *src++)
369     if (!isspace(ch))
370     break;
371     /* Make sure there is another trailing = sign. */
372     if (ch != pad)
373     return (-1);
374     ch = *src++; /* Skip the = */
375     /* Fall through to "single trailing =" case. */
376     /* FALLTHROUGH */
377    
378     case 3: /* Valid, means two bytes of info */
379     /*
380     * We know this char is an =. Is there anything but
381     * whitespace after it?
382     */
383     for (; ch != '\0'; ch = *src++)
384     if (!isspace(ch))
385     return (-1);
386    
387     /*
388     * Now make sure for cases 2 and 3 that the "extra"
389     * bits that slopped past the last full byte were
390     * zeros. If we don't check them, they become a
391     * subliminal channel.
392     */
393     if (target && target[tarindex] != 0)
394     return (-1);
395     }
396     } else {
397     /*
398     * We ended by seeing the end of the string. Make sure we
399     * have no partial bytes lying around.
400     */
401     if (state != 0)
402     return (-1);
403     }
404    
405     return (tarindex);
406     }
407    
408    
409     // SSH2���� BASE64 �`�����i�[����������
410     static Key *parse_uudecode(char *data)
411     {
412     int count;
413     unsigned char *blob = NULL;
414     int len, n;
415     Key *key = NULL;
416     char ch;
417    
418     // BASE64���������T�C�Y������
419     count = eat_base64(data);
420     len = 2 * count;
421     blob = malloc(len);
422     if (blob == NULL)
423     goto error;
424    
425     // BASE64�f�R�[�h
426     ch = data[count];
427     data[count] = '\0'; // ���������s�R�[�h������������������������������������
428     n = uudecode(data, count, blob, len);
429     data[count] = ch;
430     if (n < 0) {
431     goto error;
432     }
433    
434     key = key_from_blob(blob, n);
435     if (key == NULL)
436     goto error;
437    
438     error:
439     if (blob != NULL)
440     free(blob);
441    
442     return (key);
443     }
444    
445    
446 yutakakn 2728 static char FAR *parse_bignum(char FAR * data)
447     {
448     uint32 digits = 0;
449     BIGNUM *num = BN_new();
450     BIGNUM *billion = BN_new();
451     BIGNUM *digits_num = BN_new();
452     BN_CTX *ctx = BN_CTX_new();
453     char FAR *result;
454     int ch;
455     int leftover_digits = 1;
456    
457     BN_CTX_init(ctx);
458     BN_set_word(num, 0);
459     BN_set_word(billion, 1000000000L);
460    
461     while ((ch = *data) >= '0' && ch <= '9') {
462     if (leftover_digits == 1000000000L) {
463     BN_set_word(digits_num, digits);
464     BN_mul(num, num, billion, ctx);
465     BN_add(num, num, digits_num);
466     leftover_digits = 1;
467     digits = 0;
468     }
469    
470     digits = digits * 10 + ch - '0';
471     leftover_digits *= 10;
472     data++;
473     }
474    
475     BN_set_word(digits_num, digits);
476     BN_set_word(billion, leftover_digits);
477     BN_mul(num, num, billion, ctx);
478     BN_add(num, num, digits_num);
479    
480     result = (char FAR *) malloc(2 + BN_num_bytes(num));
481     set_ushort16_MSBfirst(result, BN_num_bits(num));
482     BN_bn2bin(num, result + 2);
483    
484     BN_CTX_free(ctx);
485     BN_free(digits_num);
486     BN_free(num);
487     BN_free(billion);
488    
489     return result;
490     }
491    
492 yutakakn 2856 //
493     // known_hosts�t�@�C�������e���������A�w�������z�X�g�����J�����T���B
494     //
495 yutakakn 2728 static int check_host_key(PTInstVar pvar, char FAR * hostname,
496     char FAR * data)
497     {
498     int index = eat_spaces(data);
499     int matched = 0;
500     int keybits = 0;
501    
502     if (data[index] == '#') {
503     return index + eat_to_end_of_line(data + index);
504     }
505    
506     /* if we find an empty line, then it won't have any patterns matching the hostname
507     and so we skip it */
508     index--;
509     do {
510     int negated;
511    
512     index++;
513     negated = data[index] == '!';
514    
515     if (negated) {
516     index++;
517     if (match_pattern(data + index, hostname)) {
518     return index + eat_to_end_of_line(data + index);
519     }
520     } else if (match_pattern(data + index, hostname)) {
521     matched = 1;
522     }
523    
524     index += eat_to_end_of_pattern(data + index);
525     } while (data[index] == ',');
526    
527     if (!matched) {
528     return index + eat_to_end_of_line(data + index);
529     } else {
530 yutakakn 2856 // ���������������t�H�[�}�b�g��������
531     // �����A���������v�����G���g�����������������������B
532     /*
533     [SSH1]
534     192.168.1.2 1024 35 13032....
535 yutakakn 2728
536 yutakakn 2856 [SSH2]
537     192.168.1.2 ssh-rsa AAAAB3NzaC1....
538     192.168.1.2 ssh-dss AAAAB3NzaC1....
539     192.168.1.2 rsa AAAAB3NzaC1....
540     192.168.1.2 dsa AAAAB3NzaC1....
541     192.168.1.2 rsa1 AAAAB3NzaC1....
542     */
543     int rsa1_key_bits;
544 yutakakn 2728
545     index += eat_spaces(data + index);
546    
547 yutakakn 2856 rsa1_key_bits = atoi(data + index);
548     if (rsa1_key_bits > 0) { // RSA1������
549     if (!SSHv1(pvar)) { // SSH2��������������������
550     return index + eat_to_end_of_line(data + index);
551     }
552 yutakakn 2728
553 yutakakn 2856 pvar->hosts_state.hostkey.type = KEY_RSA1;
554    
555     pvar->hosts_state.hostkey.bits = rsa1_key_bits;
556     index += eat_digits(data + index);
557     index += eat_spaces(data + index);
558    
559     pvar->hosts_state.hostkey.exp = parse_bignum(data + index);
560     index += eat_digits(data + index);
561     index += eat_spaces(data + index);
562    
563     pvar->hosts_state.hostkey.mod = parse_bignum(data + index);
564    
565     /*
566     if (pvar->hosts_state.key_bits < 0
567     || pvar->hosts_state.key_exp == NULL
568     || pvar->hosts_state.key_mod == NULL) {
569     pvar->hosts_state.key_bits = 0;
570     free(pvar->hosts_state.key_exp);
571     free(pvar->hosts_state.key_mod);
572     }*/
573    
574     } else {
575     char *cp, *p;
576     Key *key;
577    
578     if (!SSHv2(pvar)) { // SSH1��������������������
579     return index + eat_to_end_of_line(data + index);
580     }
581    
582     cp = data + index;
583     p = strchr(cp, ' ');
584     if (p == NULL) {
585     return index + eat_to_end_of_line(data + index);
586     }
587     index += (p - cp); // setup index
588     *p = '\0';
589     pvar->hosts_state.hostkey.type = get_keytype_from_name(cp);
590     *p = ' ';
591    
592     index += eat_spaces(data + index); // update index
593    
594     // uudecode
595     key = parse_uudecode(data + index);
596     if (key == NULL) {
597     return index + eat_to_end_of_line(data + index);
598     }
599    
600     // setup
601     pvar->hosts_state.hostkey.type = key->type;
602     pvar->hosts_state.hostkey.dsa = key->dsa;
603     pvar->hosts_state.hostkey.rsa = key->rsa;
604    
605     index += eat_base64(data + index);
606     index += eat_spaces(data + index);
607 yutakakn 2728 }
608    
609     return index + eat_to_end_of_line(data + index);
610     }
611     }
612    
613 yutakakn 2856 //
614     // known_hosts�t�@�C�������z�X�g�������v�����s������
615     //
616 yutakakn 2728 static int read_host_key(PTInstVar pvar, char FAR * hostname,
617 yutakakn 2861 int suppress_errors, int return_always)
618 yutakakn 2728 {
619     int i;
620 yutakakn 2861 int while_flg;
621 yutakakn 2728
622     for (i = 0; hostname[i] != 0; i++) {
623     int ch = hostname[i];
624    
625     if (!is_pattern_char(ch) || ch == '*' || ch == '?') {
626     if (!suppress_errors) {
627     notify_fatal_error(pvar,
628     "The host name contains an invalid character.\n"
629     "This session will be terminated.");
630     }
631     return 0;
632     }
633     }
634    
635     if (i == 0) {
636     if (!suppress_errors) {
637     notify_fatal_error(pvar, "The host name should not be empty.\n"
638     "This session will be terminated.");
639     }
640     return 0;
641     }
642    
643 yutakakn 2856 #if 0
644 yutakakn 2728 pvar->hosts_state.key_bits = 0;
645     free(pvar->hosts_state.key_exp);
646     pvar->hosts_state.key_exp = NULL;
647     free(pvar->hosts_state.key_mod);
648     pvar->hosts_state.key_mod = NULL;
649 yutakakn 2856 #else
650     // hostkey type is KEY_UNSPEC.
651     init_hostkey(&pvar->hosts_state.hostkey);
652     #endif
653 yutakakn 2728
654     do {
655     if (pvar->hosts_state.file_data == NULL
656     || pvar->hosts_state.file_data[pvar->hosts_state.
657     file_data_index] == 0) {
658     char FAR *filename;
659     int keep_going = 1;
660    
661     if (pvar->hosts_state.file_data != NULL) {
662     end_read_file(pvar, suppress_errors);
663     }
664    
665     do {
666     filename =
667     pvar->hosts_state.file_names[pvar->hosts_state.
668     file_num];
669    
670     if (filename == NULL) {
671     return 1;
672     } else {
673     pvar->hosts_state.file_num++;
674    
675     if (filename[0] != 0) {
676     if (begin_read_file
677     (pvar, filename, suppress_errors)) {
678     pvar->hosts_state.file_data_index = 0;
679     keep_going = 0;
680     }
681     }
682     }
683     } while (keep_going);
684     }
685    
686     pvar->hosts_state.file_data_index +=
687     check_host_key(pvar, hostname,
688     pvar->hosts_state.file_data +
689     pvar->hosts_state.file_data_index);
690    
691 yutakakn 2861 if (!return_always) {
692     // �L�����L�[��������������
693     while_flg = (pvar->hosts_state.hostkey.type == KEY_UNSPEC);
694     }
695     else {
696     while_flg = 0;
697     }
698     } while (while_flg);
699    
700 yutakakn 2728 return 1;
701     }
702    
703     static void finish_read_host_files(PTInstVar pvar, int suppress_errors)
704     {
705     if (pvar->hosts_state.file_data != NULL) {
706     end_read_file(pvar, suppress_errors);
707     }
708     }
709    
710 yutakakn 2856 // �T�[�o�����������O���Aknown_hosts�t�@�C�������z�X�g���J�������������������B
711 yutakakn 2728 void HOSTS_prefetch_host_key(PTInstVar pvar, char FAR * hostname)
712     {
713     if (!begin_read_host_files(pvar, 1)) {
714     return;
715     }
716    
717 yutakakn 2861 if (!read_host_key(pvar, hostname, 1, 0)) {
718 yutakakn 2728 return;
719     }
720    
721     free(pvar->hosts_state.prefetched_hostname);
722     pvar->hosts_state.prefetched_hostname = _strdup(hostname);
723    
724     finish_read_host_files(pvar, 1);
725     }
726    
727     static BOOL equal_mp_ints(unsigned char FAR * num1,
728     unsigned char FAR * num2)
729     {
730     if (num1 == NULL || num2 == NULL) {
731     return FALSE;
732     } else {
733     uint32 bytes = (get_ushort16_MSBfirst(num1) + 7) / 8;
734    
735     if (bytes != (get_ushort16_MSBfirst(num2) + 7) / 8) {
736     return FALSE; /* different byte lengths */
737     } else {
738     return memcmp(num1 + 2, num2 + 2, bytes) == 0;
739     }
740     }
741     }
742    
743 yutakakn 2856 // ���J����������������������
744     static BOOL match_key(PTInstVar pvar, Key *key)
745 yutakakn 2728 {
746 yutakakn 2856 int bits;
747     unsigned char FAR * exp;
748     unsigned char FAR * mod;
749    
750     if (key->type == KEY_RSA1) { // SSH1 host public key
751     bits = key->bits;
752     exp = key->exp;
753     mod = key->mod;
754    
755     /* just check for equal exponent and modulus */
756     return equal_mp_ints(exp, pvar->hosts_state.hostkey.exp)
757     && equal_mp_ints(mod, pvar->hosts_state.hostkey.mod);
758     /*
759     return equal_mp_ints(exp, pvar->hosts_state.key_exp)
760     && equal_mp_ints(mod, pvar->hosts_state.key_mod);
761     */
762    
763     } else if (key->type == KEY_RSA) { // SSH2 RSA host public key
764    
765     return key->rsa != NULL && pvar->hosts_state.hostkey.rsa != NULL &&
766     BN_cmp(key->rsa->e, pvar->hosts_state.hostkey.rsa->e) == 0 &&
767     BN_cmp(key->rsa->n, pvar->hosts_state.hostkey.rsa->n) == 0;
768    
769     } else { // // SSH2 DSA host public key
770    
771     return key->dsa != NULL && pvar->hosts_state.hostkey.dsa &&
772     BN_cmp(key->dsa->p, pvar->hosts_state.hostkey.dsa->p) == 0 &&
773     BN_cmp(key->dsa->q, pvar->hosts_state.hostkey.dsa->q) == 0 &&
774     BN_cmp(key->dsa->g, pvar->hosts_state.hostkey.dsa->g) == 0 &&
775     BN_cmp(key->dsa->pub_key, pvar->hosts_state.hostkey.dsa->pub_key) == 0;
776    
777     }
778    
779 yutakakn 2728 }
780    
781     static void init_hosts_dlg(PTInstVar pvar, HWND dlg)
782     {
783     char buf[1024];
784     char buf2[2048];
785     int i, j;
786     int ch;
787 yutakakn 2857 char *fp;
788 yutakakn 2728
789 yutakakn 2857 // static text�� # �������z�X�g�����u������
790 yutakakn 2728 GetDlgItemText(dlg, IDC_HOSTWARNING, buf, sizeof(buf));
791     for (i = 0; (ch = buf[i]) != 0 && ch != '#'; i++) {
792     buf2[i] = ch;
793     }
794     if (sizeof(buf2) - i - 1 > 0) {
795     strncpy(buf2 + i, pvar->hosts_state.prefetched_hostname,
796     sizeof(buf2) - i - 1);
797     }
798     j = i + strlen(buf2 + i);
799     for (; buf[i] == '#'; i++) {
800     }
801     if (sizeof(buf2) - j - 1 > 0) {
802     strncpy(buf2 + j, buf + i, sizeof(buf2) - j - 1);
803     }
804     buf2[sizeof(buf2) - 1] = 0;
805    
806     SetDlgItemText(dlg, IDC_HOSTWARNING, buf2);
807 yutakakn 2857
808     // fingerprint����������
809     fp = key_fingerprint(&pvar->hosts_state.hostkey);
810     SendMessage(GetDlgItem(dlg, IDC_FINGER_PRINT), WM_SETTEXT, 0, (LPARAM)fp);
811 yutakakn 2728 }
812    
813     static int print_mp_int(char FAR * buf, unsigned char FAR * mp)
814     {
815     int i = 0, j, k;
816     BIGNUM *num = BN_new();
817     int ch;
818    
819     BN_bin2bn(mp + 2, (get_ushort16_MSBfirst(mp) + 7) / 8, num);
820    
821     do {
822     buf[i] = (char) ((BN_div_word(num, 10)) + '0');
823     i++;
824     } while (!BN_is_zero(num));
825    
826     /* we need to reverse the digits */
827     for (j = 0, k = i - 1; j < k; j++, k--) {
828     ch = buf[j];
829     buf[j] = buf[k];
830     buf[k] = ch;
831     }
832    
833     buf[i] = 0;
834     return i;
835     }
836    
837 yutakakn 2856 //
838     // known_hosts �t�@�C�������������G���g�������������B
839     //
840 yutakakn 2728 static char FAR *format_host_key(PTInstVar pvar)
841     {
842     int host_len = strlen(pvar->hosts_state.prefetched_hostname);
843 yutakakn 2856 char *result = NULL;
844     int index;
845     enum hostkey_type type = pvar->hosts_state.hostkey.type;
846    
847     if (type == KEY_RSA1) {
848     result = (char FAR *) malloc(host_len
849 yutakakn 2728 + 50 +
850 yutakakn 2856 get_ushort16_MSBfirst(pvar->hosts_state.hostkey.exp) /
851 yutakakn 2728 3 +
852 yutakakn 2856 get_ushort16_MSBfirst(pvar->hosts_state.hostkey.mod) /
853 yutakakn 2728 3);
854    
855 yutakakn 2856 strcpy(result, pvar->hosts_state.prefetched_hostname);
856     index = host_len;
857 yutakakn 2728
858 yutakakn 2856 sprintf(result + index, " %d ", pvar->hosts_state.hostkey.bits);
859     index += strlen(result + index);
860     index += print_mp_int(result + index, pvar->hosts_state.hostkey.exp);
861     result[index] = ' ';
862     index++;
863     index += print_mp_int(result + index, pvar->hosts_state.hostkey.mod);
864     strcpy(result + index, " \r\n");
865 yutakakn 2728
866 yutakakn 2856 } else if (type == KEY_RSA || type == KEY_DSA) {
867     Key *key = &pvar->hosts_state.hostkey;
868     char *blob = NULL;
869     int blen, uulen, msize;
870     char *uu = NULL;
871     int n;
872    
873     key_to_blob(key, &blob, &blen);
874     uulen = 2 * blen;
875     uu = malloc(uulen);
876     if (uu == NULL) {
877     goto error;
878     }
879     n = uuencode(blob, blen, uu, uulen);
880     if (n > 0) {
881     msize = host_len + 50 + uulen;
882     result = malloc(msize);
883     if (result == NULL) {
884     goto error;
885     }
886    
887     // setup
888     _snprintf(result, msize, "%s %s %s\r\n",
889     pvar->hosts_state.prefetched_hostname,
890     get_sshname_from_key(key),
891     uu);
892     }
893     error:
894     if (blob != NULL)
895     free(blob);
896     if (uu != NULL)
897     free(uu);
898    
899     } else {
900     return NULL;
901    
902     }
903    
904 yutakakn 2728 return result;
905     }
906    
907     static void add_host_key(PTInstVar pvar)
908     {
909 yutakakn 2876 char buf[FILENAME_MAX];
910 yutakakn 2728 char FAR *name = pvar->hosts_state.file_names[0];
911 yutakakn 2876 get_teraterm_dir_relative_name(buf, sizeof(buf), name);
912 yutakakn 2728
913 yutakakn 2876 if (buf == NULL || buf[0] == 0) {
914 yutakakn 2728 notify_nonfatal_error(pvar,
915     "The host and its key cannot be added, because no known-hosts file has been specified.\n"
916     "Restart Teraterm and specify a read/write known-hosts file in the TTSSH Setup dialog box.");
917     } else {
918     char FAR *keydata = format_host_key(pvar);
919     int length = strlen(keydata);
920     int fd =
921 yutakakn 2876 _open(buf,
922 yutakakn 2728 _O_APPEND | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL |
923     _O_BINARY,
924     _S_IREAD | _S_IWRITE);
925     int amount_written;
926     int close_result;
927    
928     if (fd == -1) {
929     if (errno == EACCES) {
930     notify_nonfatal_error(pvar,
931     "An error occurred while trying to write the host key.\n"
932     "You do not have permission to write to the known-hosts file.");
933     } else {
934     notify_nonfatal_error(pvar,
935     "An error occurred while trying to write the host key.\n"
936     "The host key could not be written.");
937     }
938     return;
939     }
940    
941     amount_written = _write(fd, keydata, length);
942     free(keydata);
943     close_result = _close(fd);
944    
945     if (amount_written != length || close_result == -1) {
946     notify_nonfatal_error(pvar,
947     "An error occurred while trying to write the host key.\n"
948     "The host key could not be written.");
949     }
950     }
951     }
952    
953 yutakakn 2861 static char FAR *copy_mp_int(char FAR * num)
954     {
955     int len = (get_ushort16_MSBfirst(num) + 7) / 8 + 2;
956     char FAR *result = (char FAR *) malloc(len);
957    
958     if (result != NULL) {
959     memcpy(result, num, len);
960     }
961    
962     return result;
963     }
964    
965 yutakakn 2856 //
966 yutakakn 2861 // �����z�X�g�����e���������L�[����������
967     // add_host_key ����������������
968     //
969     static void delete_different_key(PTInstVar pvar)
970     {
971 yutakakn 2876 char buf[FILENAME_MAX];
972 yutakakn 2861 char FAR *name = pvar->hosts_state.file_names[0];
973 yutakakn 2876 get_teraterm_dir_relative_name(buf, sizeof(buf), name);
974 yutakakn 2861
975 yutakakn 2876 if (buf == NULL || buf[0] == 0) {
976 yutakakn 2861 notify_nonfatal_error(pvar,
977     "The host and its key cannot be added, because no known-hosts file has been specified.\n"
978     "Restart Teraterm and specify a read/write known-hosts file in the TTSSH Setup dialog box.");
979     }
980     else {
981     Key key; // ���������z�X�g���L�[
982 yutakakn 2876 int length;
983 yutakakn 2861 char filename[L_tmpnam];
984     int fd;
985     int amount_written = 0;
986     int close_result;
987     int data_index = 0;
988    
989     // �������������t�@�C�����J��
990     tmpnam(filename);
991     fd =
992     _open(filename,
993     _O_CREAT | _O_WRONLY | _O_SEQUENTIAL | _O_BINARY |
994     _O_TRUNC,
995     _S_IREAD | _S_IWRITE);
996    
997     if (fd == -1) {
998     if (errno == EACCES) {
999     notify_nonfatal_error(pvar,
1000     "An error occurred while trying to write the host key.\n"
1001     "You do not have permission to write to the known-hosts file.");
1002     } else {
1003     notify_nonfatal_error(pvar,
1004     "An error occurred while trying to write the host key.\n"
1005     "The host key could not be written.");
1006     }
1007     free(filename);
1008     return;
1009     }
1010    
1011     // ���������T�[�o���L�[����������
1012     if (pvar->hosts_state.hostkey.type == KEY_RSA1) { // SSH1
1013     key.type = KEY_RSA1;
1014     key.bits = pvar->hosts_state.hostkey.bits;
1015     key.exp = copy_mp_int(pvar->hosts_state.hostkey.exp);
1016     key.mod = copy_mp_int(pvar->hosts_state.hostkey.mod);
1017     } else if (pvar->hosts_state.hostkey.type == KEY_RSA) { // SSH2 RSA
1018     key.type = KEY_RSA;
1019     key.rsa = duplicate_RSA(pvar->hosts_state.hostkey.rsa);
1020     } else { // SSH2 DSA
1021     key.type = KEY_DSA;
1022     key.dsa = duplicate_DSA(pvar->hosts_state.hostkey.dsa);
1023     }
1024    
1025     // �t�@�C��������������
1026     begin_read_host_files(pvar, 0);
1027     do {
1028     int host_index = 0;
1029     int matched = 0;
1030     int keybits = 0;
1031     char FAR *data;
1032     int do_write = 0;
1033     length = amount_written = 0;
1034    
1035     if (!read_host_key(pvar, pvar->ssh_state.hostname, 0, 1)) {
1036     break;
1037     }
1038    
1039     if (data_index == pvar->hosts_state.file_data_index) {
1040     // index ���i������ == ��������������
1041     break;
1042     }
1043    
1044     data = pvar->hosts_state.file_data + data_index;
1045     host_index = eat_spaces(data);
1046    
1047     if (data[host_index] == '#') {
1048     do_write = 1;
1049     }
1050     else {
1051     // �z�X�g������
1052     host_index--;
1053     do {
1054     int negated;
1055    
1056     host_index++;
1057     negated = data[host_index] == '!';
1058    
1059     if (negated) {
1060     host_index++;
1061     if (match_pattern(data + host_index,
1062     pvar->ssh_state.hostname)) {
1063     matched = 0;
1064     // �����o�[�W�����`�F�b�N�������� host_index ���i��������������
1065     host_index--;
1066     do {
1067     host_index++;
1068     host_index += eat_to_end_of_pattern(data + host_index);
1069     } while (data[host_index] == ',');
1070     break;
1071     }
1072     }
1073     else if (match_pattern(data + host_index,
1074     pvar->ssh_state.hostname)) {
1075     matched = 1;
1076     }
1077     host_index += eat_to_end_of_pattern(data + host_index);
1078     } while (data[host_index] == ',');
1079    
1080     // �z�X�g�������������v�����L�[����������
1081     if (match_key(pvar, &key)) {
1082     do_write = 1;
1083     }
1084     // �z�X�g������������
1085     else if (!matched) {
1086     do_write = 1;
1087     }
1088     // �z�X�g�������� and �������o�[�W����������
1089     else {
1090     int rsa1_key_bits=0;
1091     rsa1_key_bits = atoi(data + host_index + eat_spaces(data + host_index));
1092    
1093     if (rsa1_key_bits > 0) { // �t�@�C�����L�[�� ssh1
1094     if (!SSHv1(pvar)) {
1095     do_write = 1;
1096     }
1097     }
1098     else { // �t�@�C�����L�[�� ssh2
1099     if (!SSHv2(pvar)) {
1100     do_write = 1;
1101     }
1102     }
1103     }
1104     }
1105    
1106     // ������������
1107     if (do_write) {
1108     length = pvar->hosts_state.file_data_index - data_index;
1109     amount_written =
1110     _write(fd, pvar->hosts_state.file_data + data_index,
1111     length);
1112    
1113     if (amount_written != length) {
1114     goto error1;
1115     }
1116     }
1117     data_index = pvar->hosts_state.file_data_index;
1118     } while (1); // ������������
1119    
1120     error1:
1121     close_result = _close(fd);
1122     if (amount_written != length || close_result == -1) {
1123     notify_nonfatal_error(pvar,
1124     "An error occurred while trying to write the host key.\n"
1125     "The host key could not be written.");
1126     goto error2;
1127     }
1128    
1129     // �������������t�@�C���������l�[��
1130 yutakakn 2876 _unlink(buf);
1131     rename(filename, buf);
1132 yutakakn 2861
1133     error2:
1134     _unlink(filename);
1135    
1136     finish_read_host_files(pvar, 0);
1137     }
1138     }
1139    
1140     //
1141 yutakakn 2856 // Unknown host���z�X�g���J���� known_hosts �t�@�C����������������������
1142     // ���[�U���m�F�������B
1143     // TODO: finger print���\�����s���B
1144     // (2006.3.25 yutaka)
1145     //
1146 yutakakn 2861 static BOOL CALLBACK hosts_add_dlg_proc(HWND dlg, UINT msg, WPARAM wParam,
1147     LPARAM lParam)
1148 yutakakn 2728 {
1149     PTInstVar pvar;
1150    
1151     switch (msg) {
1152     case WM_INITDIALOG:
1153     pvar = (PTInstVar) lParam;
1154     pvar->hosts_state.hosts_dialog = dlg;
1155     SetWindowLong(dlg, DWL_USER, lParam);
1156    
1157     init_hosts_dlg(pvar, dlg);
1158 yutakakn 2856
1159     // add host check box���`�F�b�N���f�t�H���g������������
1160     SendMessage(GetDlgItem(dlg, IDC_ADDTOKNOWNHOSTS), BM_SETCHECK, BST_CHECKED, 0);
1161    
1162 yutakakn 2728 return TRUE; /* because we do not set the focus */
1163    
1164     case WM_COMMAND:
1165     pvar = (PTInstVar) GetWindowLong(dlg, DWL_USER);
1166    
1167     switch (LOWORD(wParam)) {
1168     case IDC_CONTINUE:
1169     if (IsDlgButtonChecked(dlg, IDC_ADDTOKNOWNHOSTS)) {
1170     add_host_key(pvar);
1171     }
1172    
1173 yutakakn 2856 if (SSHv1(pvar)) {
1174     SSH_notify_host_OK(pvar);
1175     } else { // SSH2
1176     // SSH2���������� SSH_notify_host_OK() �������B
1177     }
1178    
1179 yutakakn 2728 pvar->hosts_state.hosts_dialog = NULL;
1180    
1181     EndDialog(dlg, 1);
1182     return TRUE;
1183    
1184     case IDCANCEL: /* kill the connection */
1185     pvar->hosts_state.hosts_dialog = NULL;
1186     notify_closed_connection(pvar);
1187     EndDialog(dlg, 0);
1188     return TRUE;
1189    
1190     default:
1191     return FALSE;
1192     }
1193    
1194     default:
1195     return FALSE;
1196     }
1197     }
1198    
1199 yutakakn 2861 //
1200     // �u�����������m�F�_�C�A���O������
1201     //
1202     static BOOL CALLBACK hosts_replace_dlg_proc(HWND dlg, UINT msg, WPARAM wParam,
1203     LPARAM lParam)
1204 yutakakn 2728 {
1205 yutakakn 2861 PTInstVar pvar;
1206 yutakakn 2728
1207 yutakakn 2861 switch (msg) {
1208     case WM_INITDIALOG:
1209     pvar = (PTInstVar) lParam;
1210     pvar->hosts_state.hosts_dialog = dlg;
1211     SetWindowLong(dlg, DWL_USER, lParam);
1212    
1213     init_hosts_dlg(pvar, dlg);
1214    
1215     // �f�t�H���g���`�F�b�N����������
1216     return TRUE; /* because we do not set the focus */
1217    
1218     case WM_COMMAND:
1219     pvar = (PTInstVar) GetWindowLong(dlg, DWL_USER);
1220    
1221     switch (LOWORD(wParam)) {
1222     case IDC_CONTINUE:
1223     if (IsDlgButtonChecked(dlg, IDC_ADDTOKNOWNHOSTS)) {
1224     add_host_key(pvar);
1225     delete_different_key(pvar);
1226     }
1227    
1228     if (SSHv1(pvar)) {
1229     SSH_notify_host_OK(pvar);
1230     } else { // SSH2
1231     // SSH2���������� SSH_notify_host_OK() �������B
1232     }
1233    
1234     pvar->hosts_state.hosts_dialog = NULL;
1235    
1236     EndDialog(dlg, 1);
1237     return TRUE;
1238    
1239     case IDCANCEL: /* kill the connection */
1240     pvar->hosts_state.hosts_dialog = NULL;
1241     notify_closed_connection(pvar);
1242     EndDialog(dlg, 0);
1243     return TRUE;
1244    
1245     default:
1246     return FALSE;
1247     }
1248    
1249     default:
1250     return FALSE;
1251 yutakakn 2728 }
1252     }
1253    
1254     void HOSTS_do_unknown_host_dialog(HWND wnd, PTInstVar pvar)
1255     {
1256     if (pvar->hosts_state.hosts_dialog == NULL) {
1257     HWND cur_active = GetActiveWindow();
1258    
1259     DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SSHUNKNOWNHOST),
1260     cur_active != NULL ? cur_active : wnd,
1261 yutakakn 2861 hosts_add_dlg_proc, (LPARAM) pvar);
1262 yutakakn 2728 }
1263     }
1264    
1265     void HOSTS_do_different_host_dialog(HWND wnd, PTInstVar pvar)
1266     {
1267     if (pvar->hosts_state.hosts_dialog == NULL) {
1268     HWND cur_active = GetActiveWindow();
1269    
1270     DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SSHDIFFERENTHOST),
1271     cur_active != NULL ? cur_active : wnd,
1272 yutakakn 2861 hosts_replace_dlg_proc, (LPARAM) pvar);
1273 yutakakn 2728 }
1274     }
1275    
1276 yutakakn 2856 //
1277     // �T�[�o�����������������z�X�g���J�������������`�F�b�N����
1278     //
1279     // SSH2���������� (2006.3.24 yutaka)
1280     //
1281     BOOL HOSTS_check_host_key(PTInstVar pvar, char FAR * hostname, Key *key)
1282 yutakakn 2728 {
1283     int found_different_key = 0;
1284    
1285 yutakakn 2856 // ������ known_hosts �t�@�C�������z�X�g���J�����������������������A���������r�����B
1286 yutakakn 2728 if (pvar->hosts_state.prefetched_hostname != NULL
1287 yutakakn 2850 && _stricmp(pvar->hosts_state.prefetched_hostname, hostname) == 0
1288 yutakakn 2856 && match_key(pvar, key)) {
1289    
1290     if (SSHv1(pvar)) {
1291     SSH_notify_host_OK(pvar);
1292     } else {
1293     // SSH2���������� SSH_notify_host_OK() �������B
1294     }
1295 yutakakn 2728 return TRUE;
1296     }
1297    
1298 yutakakn 2856 // �������������������������A�������_���t�@�C��������������
1299 yutakakn 2728 if (begin_read_host_files(pvar, 0)) {
1300     do {
1301 yutakakn 2861 if (!read_host_key(pvar, hostname, 0, 0)) {
1302 yutakakn 2728 break;
1303     }
1304    
1305 yutakakn 2856 if (pvar->hosts_state.hostkey.type != KEY_UNSPEC) {
1306     if (match_key(pvar, key)) {
1307 yutakakn 2728 finish_read_host_files(pvar, 0);
1308 yutakakn 2859 // ���������G���g�����Q�������A���v�����L�[�������������������B
1309     // SSH2���������������������������B(2006.3.29 yutaka)
1310     if (SSHv1(pvar)) {
1311     SSH_notify_host_OK(pvar);
1312     } else {
1313     // SSH2���������� SSH_notify_host_OK() �������B
1314     }
1315 yutakakn 2728 return TRUE;
1316     } else {
1317 yutakakn 2856 // �L�[�� known_hosts ���������������A�L�[�����e���������B
1318 yutakakn 2728 found_different_key = 1;
1319     }
1320     }
1321 yutakakn 2856 } while (pvar->hosts_state.hostkey.type != KEY_UNSPEC); // �L�[�����������������������[�v����
1322 yutakakn 2728
1323     finish_read_host_files(pvar, 0);
1324     }
1325    
1326 yutakakn 2856
1327     // known_hosts �������������L�[���������t�@�C�������������������A�������������������B
1328     pvar->hosts_state.hostkey.type = key->type;
1329     if (key->type == KEY_RSA1) { // SSH1
1330     pvar->hosts_state.hostkey.bits = key->bits;
1331     pvar->hosts_state.hostkey.exp = copy_mp_int(key->exp);
1332     pvar->hosts_state.hostkey.mod = copy_mp_int(key->mod);
1333    
1334     } else if (key->type == KEY_RSA) { // SSH2 RSA
1335     pvar->hosts_state.hostkey.rsa = duplicate_RSA(key->rsa);
1336    
1337     } else { // SSH2 DSA
1338     pvar->hosts_state.hostkey.dsa = duplicate_DSA(key->dsa);
1339    
1340     }
1341 yutakakn 2728 free(pvar->hosts_state.prefetched_hostname);
1342     pvar->hosts_state.prefetched_hostname = _strdup(hostname);
1343    
1344     if (found_different_key) {
1345     PostMessage(pvar->NotificationWindow, WM_COMMAND,
1346     ID_SSHDIFFERENTHOST, 0);
1347     } else {
1348     PostMessage(pvar->NotificationWindow, WM_COMMAND,
1349     ID_SSHUNKNOWNHOST, 0);
1350     }
1351    
1352     return TRUE;
1353     }
1354    
1355     void HOSTS_notify_disconnecting(PTInstVar pvar)
1356     {
1357     if (pvar->hosts_state.hosts_dialog != NULL) {
1358     PostMessage(pvar->hosts_state.hosts_dialog, WM_COMMAND, IDCANCEL,
1359     0);
1360     /* the main window might not go away if it's not enabled. (see vtwin.cpp) */
1361     EnableWindow(pvar->NotificationWindow, TRUE);
1362     }
1363     }
1364    
1365     void HOSTS_end(PTInstVar pvar)
1366     {
1367     int i;
1368    
1369     free(pvar->hosts_state.prefetched_hostname);
1370 yutakakn 2856 #if 0
1371 yutakakn 2728 free(pvar->hosts_state.key_exp);
1372     free(pvar->hosts_state.key_mod);
1373 yutakakn 2856 #else
1374     init_hostkey(&pvar->hosts_state.hostkey);
1375     #endif
1376 yutakakn 2728
1377     if (pvar->hosts_state.file_names != NULL) {
1378     for (i = 0; pvar->hosts_state.file_names[i] != NULL; i++) {
1379     free(pvar->hosts_state.file_names[i]);
1380     }
1381     free(pvar->hosts_state.file_names);
1382     }
1383     }
1384 yutakakn 2761
1385     /*
1386     * $Log: not supported by cvs2svn $
1387 yutakakn 2876 * Revision 1.7 2006/04/04 13:52:52 yutakakn
1388     * known_hosts�t�@�C�����������L�[�����������������z�X�g���G���g���������������A�����L�[�����������@�\�����������B
1389     *
1390 yutakakn 2861 * Revision 1.6 2006/03/29 14:56:52 yutakakn
1391     * known_hosts�t�@�C�����L�[�����������������z�X�g���G���g�����������A�A�v���P�[�V�����G���[�������o�O���C�������B
1392     *
1393 yutakakn 2859 * Revision 1.5 2006/03/26 17:07:17 yutakakn
1394     * fingerprint�\��������
1395     *
1396 yutakakn 2857 * Revision 1.4 2006/03/26 15:43:58 yutakakn
1397     * SSH2��known_hosts���������������B
1398     *
1399 yutakakn 2856 * Revision 1.3 2006/02/18 07:37:02 yutakakn
1400     * �E�R���p�C���� Visual Studio 2005 Standard Edition �������������B
1401     * �Estricmp()��_stricmp()���u������
1402     * �Estrdup()��_strdup()���u������
1403     *
1404 yutakakn 2850 * Revision 1.2 2004/12/19 15:39:42 yutakakn
1405     * CVS LogID������
1406     *
1407 yutakakn 2761 */

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26