Develop and Download Open Source Software

Browse Subversion Repository

Contents of /branches/ssh_chacha20poly1305/ttssh2/ttxssh/ttxssh.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2831 - (show annotations) (download) (as text)
Sat Jul 9 05:16:06 2005 UTC (18 years, 9 months ago) by yutakakn
Original Path: ttssh2/trunk/ttxssh/ttxssh.c
File MIME type: text/x-csrc
File size: 84346 byte(s)
OpenSSL 0.9.8でビルドできるようにした。

1 /*
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 /* Teraterm extension mechanism
30 Robert O'Callahan (roc+tt@cs.cmu.edu)
31
32 Teraterm by Takashi Teranishi (teranishi@rikaxp.riken.go.jp)
33 */
34
35 #include "ttxssh.h"
36 #include "fwdui.h"
37 #include "util.h"
38 #include "ssh.h"
39
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <io.h>
44 #include <fcntl.h>
45 #include <sys/stat.h>
46 #include <time.h>
47
48 #include "resource.h"
49 #include <commctrl.h>
50 #include <commdlg.h>
51 #ifdef INET6
52 #include <winsock2.h>
53 static char FAR *ProtocolFamilyList[] = { "UNSPEC", "IPv6", "IPv4", NULL };
54 #else
55 #include <winsock.h>
56 #endif /* INET6 */
57
58 #include <Lmcons.h>
59
60 // include OpenSSL header file
61 #include <openssl/opensslv.h>
62 #include <openssl/evp.h>
63 #include <openssl/rsa.h>
64 #include <openssl/dsa.h>
65 #include <openssl/bn.h>
66 #include <openssl/pem.h>
67 #include <openssl/rand.h>
68 #include <openssl/rc4.h>
69 #include <openssl/md5.h>
70
71 // include ZLib header file
72 #include <zlib.h>
73
74 #include "buffer.h"
75 #include "cipher.h"
76
77 #define MATCH_STR(s, o) _strnicmp((s), (o), NUM_ELEM(o) - 1)
78
79 /* This extension implements SSH, so we choose a load order in the
80 "protocols" range. */
81 #define ORDER 2500
82
83 #ifdef TERATERM32
84 static HICON SecureIcon = NULL;
85 #endif
86
87 static TInstVar FAR *pvar;
88
89 #ifdef TERATERM32
90 /* WIN32 allows multiple instances of a DLL */
91 static TInstVar InstVar;
92 #define GET_VAR()
93 #else
94 /* WIN16 does not allow multiple instances of a DLL */
95
96 /* maximum number of Tera Term instances */
97 #define MAXNUMINST 32
98 /* list of task handles for Tera Term instances */
99 static HANDLE FAR TaskList[MAXNUMINST];
100 /* variable sets for instances */
101 static TInstVar FAR *FAR InstVar[MAXNUMINST];
102
103 /* Here's how the TS settings work.
104 Whenever the TS settings are read or written to the INI file, then
105 the shared memory containing those settings is updated.
106 When Teraterm starts, the shared memory is read to initialize the TS
107 settings. */
108
109 /* TS settings shared across instances */
110 static TS_SSH ts_SSH_settings;
111
112
113 extern void SSH2_update_cipher_myproposal(PTInstVar pvar);
114
115
116 static BOOL NewVar()
117 {
118 int i = 0;
119 HANDLE Task = GetCurrentTask();
120
121 if (TaskList[0] == NULL)
122
123 if (Task == NULL)
124 return FALSE;
125 while ((i < MAXNUMINST) && (TaskList[i] != NULL))
126 i++;
127 if (i >= MAXNUMINST)
128 return FALSE;
129 pvar = (TInstVar FAR *) malloc(sizeof(TInstVar));
130 InstVar[i] = pvar;
131 TaskList[i] = Task;
132 return TRUE;
133 }
134
135 void DelVar()
136 {
137 int i = 0;
138 HANDLE Task = GetCurrentTask();
139
140 if (Task == NULL)
141 return;
142 while ((i < MAXNUMINST) && (TaskList[i] != Task))
143 i++;
144 if (i >= MAXNUMINST)
145 return;
146 free(TaskList[i]);
147 TaskList[i] = NULL;
148 }
149
150 BOOL GetVar()
151 {
152 int i = 0;
153 HANDLE Task = GetCurrentTask();
154
155 if (Task == NULL)
156 return FALSE;
157 while ((i < MAXNUMINST) && (TaskList[i] != Task))
158 i++;
159 if (i >= MAXNUMINST)
160 return FALSE;
161 pvar = InstVar[i];
162 return TRUE;
163 }
164
165 #define GET_VAR() if (!GetVar()) return
166 #endif
167
168 /*
169 This code makes lots of assumptions about the order in which Teraterm
170 does things, and how. A key assumption is that the Notification window
171 passed into WSAAsyncSelect is the main terminal window. We also assume
172 that the socket used in the first WSAconnect is the main session socket.
173 */
174
175 static void init_TTSSH(PTInstVar pvar)
176 {
177 pvar->socket = INVALID_SOCKET;
178 pvar->OldLargeIcon = NULL;
179 pvar->OldSmallIcon = NULL;
180 pvar->NotificationWindow = NULL;
181 pvar->hostdlg_activated = FALSE;
182 pvar->socket = INVALID_SOCKET;
183 pvar->NotificationWindow = NULL;
184 pvar->protocol_major = 0;
185 pvar->protocol_minor = 0;
186
187 PKT_init(pvar);
188 SSH_init(pvar);
189 CRYPT_init(pvar);
190 AUTH_init(pvar);
191 HOSTS_init(pvar);
192 FWD_init(pvar);
193 FWDUI_init(pvar);
194
195 ssh_heartbeat_lock_initialize();
196 }
197
198 static void uninit_TTSSH(PTInstVar pvar)
199 {
200 halt_ssh_heartbeat_thread(pvar);
201
202 ssh2_channel_free();
203
204 SSH_end(pvar);
205 PKT_end(pvar);
206 AUTH_end(pvar);
207 CRYPT_end(pvar);
208 HOSTS_end(pvar);
209 FWD_end(pvar);
210 FWDUI_end(pvar);
211
212 if (pvar->OldLargeIcon != NULL) {
213 PostMessage(pvar->NotificationWindow, WM_SETICON, ICON_BIG,
214 (LPARAM) pvar->OldLargeIcon);
215 pvar->OldLargeIcon = NULL;
216 }
217 if (pvar->OldSmallIcon != NULL) {
218 PostMessage(pvar->NotificationWindow, WM_SETICON, ICON_SMALL,
219 (LPARAM) pvar->OldSmallIcon);
220 pvar->OldSmallIcon = NULL;
221 }
222
223 ssh_heartbeat_lock_finalize();
224 }
225
226 static void PASCAL FAR TTXInit(PTTSet ts, PComVar cv)
227 {
228 #ifndef TERATERM32
229 if (!NewVar())
230 return; /* should be called first */
231 pvar->ts_SSH = &ts_SSH_settings;
232 #endif
233 pvar->settings = *pvar->ts_SSH;
234 pvar->ts = ts;
235 pvar->cv = cv;
236 pvar->fatal_error = FALSE;
237 pvar->showing_err = FALSE;
238 pvar->err_msg = NULL;
239
240 init_TTSSH(pvar);
241 }
242
243 static void normalize_cipher_order(char FAR * buf)
244 {
245 char ciphers_listed[SSH_CIPHER_MAX + 1];
246 char ciphers_allowed[SSH_CIPHER_MAX + 1];
247 int i, j;
248
249 /* SSH_CIPHER_NONE means that all ciphers below that one are disabled.
250 We *never* allow no encryption. */
251 #if 0
252 static char default_ciphers[] = {
253 SSH_CIPHER_3DES,
254 SSH_CIPHER_NONE,
255 SSH_CIPHER_DES, SSH_CIPHER_BLOWFISH
256 };
257 #else
258 // for SSH2(yutaka)
259 static char default_ciphers[] = {
260 SSH_CIPHER_AES128,
261 SSH_CIPHER_3DES_CBC,
262 SSH_CIPHER_3DES,
263 SSH_CIPHER_NONE,
264 SSH_CIPHER_DES, SSH_CIPHER_BLOWFISH
265 };
266 #endif
267
268 memset(ciphers_listed, 0, sizeof(ciphers_listed));
269
270 memset(ciphers_allowed, 0, sizeof(ciphers_allowed));
271 for (i = 0; i < NUM_ELEM(default_ciphers); i++) {
272 ciphers_allowed[default_ciphers[i]] = 1;
273 }
274
275 for (i = 0; buf[i] != 0; i++) {
276 int cipher_num = buf[i] - '0';
277
278 if (cipher_num < 0 || cipher_num > SSH_CIPHER_MAX
279 || !ciphers_allowed[cipher_num]
280 || ciphers_listed[cipher_num]) {
281 memmove(buf + i, buf + i + 1, strlen(buf + i + 1) + 1);
282 i--;
283 } else {
284 ciphers_listed[cipher_num] = 1;
285 }
286 }
287
288 for (j = 0; j < NUM_ELEM(default_ciphers); j++) {
289 int cipher_num = default_ciphers[j];
290
291 if (!ciphers_listed[cipher_num]) {
292 buf[i] = cipher_num + '0';
293 i++;
294 }
295 }
296
297 buf[i] = 0;
298 }
299
300 /* Remove local settings from the shared memory block. */
301 static void clear_local_settings(PTInstVar pvar)
302 {
303 pvar->ts_SSH->TryDefaultAuth = FALSE;
304 }
305
306 static BOOL read_BOOL_option(PCHAR fileName, char FAR * keyName, BOOL def)
307 {
308 char buf[1024];
309
310 buf[0] = 0;
311 GetPrivateProfileString("TTSSH", keyName, "", buf, sizeof(buf),
312 fileName);
313 if (buf[0] == 0) {
314 return def;
315 } else {
316 return atoi(buf) != 0 ||
317 stricmp(buf, "yes") == 0 || stricmp(buf, "y") == 0;
318 }
319 }
320
321 static void read_string_option(PCHAR fileName, char FAR * keyName,
322 char FAR * def, char FAR * buf, int bufSize)
323 {
324
325 buf[0] = 0;
326 GetPrivateProfileString("TTSSH", keyName, def, buf, bufSize, fileName);
327 }
328
329 static void read_ssh_options(PTInstVar pvar, PCHAR fileName)
330 {
331 extern void SSH2_update_cipher_myproposal(PTInstVar pvar);
332 char buf[1024];
333 TS_SSH FAR *settings = pvar->ts_SSH;
334
335 #define READ_STD_STRING_OPTION(name) \
336 read_string_option(fileName, #name, "", settings->name, sizeof(settings->name))
337
338 settings->Enabled = read_BOOL_option(fileName, "Enabled", FALSE);
339
340 buf[0] = 0;
341 GetPrivateProfileString("TTSSH", "Compression", "", buf, sizeof(buf),
342 fileName);
343 settings->CompressionLevel = atoi(buf);
344 if (settings->CompressionLevel < 0 || settings->CompressionLevel > 9) {
345 settings->CompressionLevel = 0;
346 }
347
348 READ_STD_STRING_OPTION(DefaultUserName);
349 READ_STD_STRING_OPTION(DefaultForwarding);
350 READ_STD_STRING_OPTION(DefaultRhostsLocalUserName);
351 READ_STD_STRING_OPTION(DefaultRhostsHostPrivateKeyFile);
352 READ_STD_STRING_OPTION(DefaultRSAPrivateKeyFile);
353
354 READ_STD_STRING_OPTION(CipherOrder);
355 normalize_cipher_order(settings->CipherOrder);
356 SSH2_update_cipher_myproposal(pvar); // yutaka
357
358 read_string_option(fileName, "KnownHostsFiles", "ssh_known_hosts",
359 settings->KnownHostsFiles,
360 sizeof(settings->KnownHostsFiles));
361
362 buf[0] = 0;
363 GetPrivateProfileString("TTSSH", "DefaultAuthMethod", "", buf,
364 sizeof(buf), fileName);
365 settings->DefaultAuthMethod = atoi(buf);
366 if (settings->DefaultAuthMethod != SSH_AUTH_PASSWORD
367 && settings->DefaultAuthMethod != SSH_AUTH_RSA
368 && settings->DefaultAuthMethod != SSH_AUTH_TIS // add (2005.3.12 yutaka)
369 && settings->DefaultAuthMethod != SSH_AUTH_RHOSTS) {
370 /* this default can never be SSH_AUTH_RHOSTS_RSA because that is not a
371 selection in the dialog box; SSH_AUTH_RHOSTS_RSA is automatically chosen
372 when the dialog box has rhosts selected and an host private key file
373 is supplied. */
374 settings->DefaultAuthMethod = SSH_AUTH_PASSWORD;
375 }
376
377 buf[0] = 0;
378 GetPrivateProfileString("TTSSH", "LogLevel", "", buf, sizeof(buf),
379 fileName);
380 settings->LogLevel = atoi(buf);
381
382 buf[0] = 0;
383 GetPrivateProfileString("TTSSH", "WriteBufferSize", "", buf,
384 sizeof(buf), fileName);
385 settings->WriteBufferSize = atoi(buf);
386 if (settings->WriteBufferSize <= 0) {
387 settings->WriteBufferSize = 2 * 1024 * 1024;
388 }
389
390 settings->LocalForwardingIdentityCheck =
391 read_BOOL_option(fileName, "LocalForwardingIdentityCheck", TRUE);
392
393 // SSH protocol version (2004.10.11 yutaka)
394 // default is SSH2 (2004.11.30 yutaka)
395 settings->ssh_protocol_version = GetPrivateProfileInt("TTSSH", "ProtocolVersion", 2, fileName);
396
397 // SSH heartbeat time(second) (2004.12.11 yutaka)
398 settings->ssh_heartbeat_overtime = GetPrivateProfileInt("TTSSH", "HeartBeat", 60, fileName);
399
400 // SSH2 keyboard-interactive (2005.1.23 yutaka)
401 // �f�t�H���g���������������BOpenSSH 4.0����keyboard-interactive���\�b�h�����`�������������������A
402 // ���Y���\�b�h���g�����R�l�N�V���������������������B(2005.3.12 yutaka)
403 settings->ssh2_keyboard_interactive = GetPrivateProfileInt("TTSSH", "KeyboardInteractive", 0, fileName);
404
405 clear_local_settings(pvar);
406 }
407
408 static void write_ssh_options(PTInstVar pvar, PCHAR fileName,
409 TS_SSH FAR * settings)
410 {
411 char buf[1024];
412
413 WritePrivateProfileString("TTSSH", "Enabled",
414 settings->Enabled ? "1" : "0", fileName);
415
416 _itoa(settings->CompressionLevel, buf, 10);
417 WritePrivateProfileString("TTSSH", "Compression", buf, fileName);
418
419 WritePrivateProfileString("TTSSH", "DefaultUserName",
420 settings->DefaultUserName, fileName);
421
422 WritePrivateProfileString("TTSSH", "DefaultForwarding",
423 settings->DefaultForwarding, fileName);
424
425 WritePrivateProfileString("TTSSH", "CipherOrder",
426 settings->CipherOrder, fileName);
427
428 WritePrivateProfileString("TTSSH", "KnownHostsFiles",
429 settings->KnownHostsFiles, fileName);
430
431 WritePrivateProfileString("TTSSH", "DefaultRhostsLocalUserName",
432 settings->DefaultRhostsLocalUserName,
433 fileName);
434
435 WritePrivateProfileString("TTSSH", "DefaultRhostsHostPrivateKeyFile",
436 settings->DefaultRhostsHostPrivateKeyFile,
437 fileName);
438
439 WritePrivateProfileString("TTSSH", "DefaultRSAPrivateKeyFile",
440 settings->DefaultRSAPrivateKeyFile,
441 fileName);
442
443 _itoa(settings->DefaultAuthMethod, buf, 10);
444 WritePrivateProfileString("TTSSH", "DefaultAuthMethod", buf, fileName);
445
446 _itoa(settings->LogLevel, buf, 10);
447 WritePrivateProfileString("TTSSH", "LogLevel", buf, fileName);
448
449 _itoa(settings->WriteBufferSize, buf, 10);
450 WritePrivateProfileString("TTSSH", "WriteBufferSize", buf, fileName);
451
452 WritePrivateProfileString("TTSSH", "LocalForwardingIdentityCheck",
453 settings->
454 LocalForwardingIdentityCheck ? "1" : "0",
455 fileName);
456
457 // SSH protocol version (2004.10.11 yutaka)
458 WritePrivateProfileString("TTSSH", "ProtocolVersion",
459 settings->ssh_protocol_version==2 ? "2" : "1",
460 fileName);
461
462 // SSH heartbeat time(second) (2004.12.11 yutaka)
463 _snprintf(buf, sizeof(buf), "%d", settings->ssh_heartbeat_overtime);
464 WritePrivateProfileString("TTSSH", "HeartBeat", buf, fileName);
465
466 // SSH2 keyboard-interactive (2005.1.23 yutaka)
467 WritePrivateProfileString("TTSSH", "KeyboardInteractive",
468 settings->ssh2_keyboard_interactive ? "1" : "0",
469 fileName);
470
471 }
472
473
474 /* find free port in all protocol family */
475 static unsigned short find_local_port(PTInstVar pvar)
476 {
477 int tries;
478 #ifdef INET6
479 SOCKET connecter;
480 struct addrinfo hints;
481 struct addrinfo FAR *res;
482 struct addrinfo FAR *res0;
483 unsigned short port;
484 char pname[NI_MAXHOST];
485 #endif /* INET6 */
486
487 if (pvar->session_settings.DefaultAuthMethod != SSH_AUTH_RHOSTS) {
488 return 0;
489 }
490
491 /* The random numbers here are only used to try to get fresh
492 ports across runs (dangling ports can cause bind errors
493 if we're unlucky). They do not need to be (and are not)
494 cryptographically strong.
495 */
496 srand((unsigned) GetTickCount());
497
498 #ifdef INET6
499 for (tries = 20; tries > 0; tries--) {
500 memset(&hints, 0, sizeof(hints));
501 hints.ai_family = pvar->ts->ProtocolFamily;
502 hints.ai_flags = AI_PASSIVE;
503 hints.ai_socktype = SOCK_STREAM;
504 port = (unsigned) rand() % 512 + 512;
505 _snprintf(pname, sizeof(pname), "%d", (int) port);
506 if (getaddrinfo(NULL, pname, &hints, &res0)) {
507 return 0;
508 /* NOT REACHED */
509 }
510
511 for (res = res0; res; res = res->ai_next) {
512 if (res->ai_family == AF_INET || res->ai_family == AF_INET6)
513 continue;
514
515 connecter =
516 socket(res->ai_family, res->ai_socktype, res->ai_protocol);
517 if (connecter == INVALID_SOCKET) {
518 freeaddrinfo(res0);
519 return 0;
520 }
521
522 if (bind(connecter, res->ai_addr, res->ai_addrlen) !=
523 SOCKET_ERROR) {
524 return port;
525 freeaddrinfo(res0);
526 closesocket(connecter);
527 } else if (WSAGetLastError() != WSAEADDRINUSE) {
528 closesocket(connecter);
529 freeaddrinfo(res0);
530 return 0;
531 }
532
533 closesocket(connecter);
534 }
535 freeaddrinfo(res0);
536 }
537
538 return 0;
539 #else
540 for (tries = 20; tries > 0; tries--) {
541 SOCKET connecter = socket(AF_INET, SOCK_STREAM, 0);
542 struct sockaddr_in connecter_addr;
543
544 connecter_addr.sin_family = AF_INET;
545 connecter_addr.sin_port = (unsigned) rand() % 512 + 512;
546 connecter_addr.sin_addr.s_addr = htonl(INADDR_ANY);
547
548 if (connecter == INVALID_SOCKET) {
549 return 0;
550 }
551
552 if (bind
553 (connecter, (struct sockaddr FAR *) &connecter_addr,
554 sizeof(connecter_addr)) != SOCKET_ERROR) {
555 closesocket(connecter);
556 return connecter_addr.sin_port;
557 } else if (WSAGetLastError() != WSAEADDRINUSE) {
558 closesocket(connecter);
559 return 0;
560 }
561
562 closesocket(connecter);
563 }
564
565 return 0;
566 #endif /* INET6 */
567 }
568
569 static int PASCAL FAR TTXconnect(SOCKET s,
570 const struct sockaddr FAR * name,
571 int namelen)
572 {
573 GET_VAR();
574
575 #ifdef INET6
576 if (pvar->socket == INVALID_SOCKET) {
577 struct sockaddr_storage ss;
578 int len;
579
580 pvar->socket = s;
581
582 memset(&ss, 0, sizeof(ss));
583 switch (pvar->ts->ProtocolFamily) {
584 case AF_INET:
585 len = sizeof(struct sockaddr_in);
586 ((struct sockaddr_in FAR *) &ss)->sin_family = AF_INET;
587 ((struct sockaddr_in FAR *) &ss)->sin_addr.s_addr = INADDR_ANY;
588 ((struct sockaddr_in FAR *) &ss)->sin_port =
589 htons(find_local_port(pvar));
590 break;
591 case AF_INET6:
592 len = sizeof(struct sockaddr_in6);
593 ((struct sockaddr_in6 FAR *) &ss)->sin6_family = AF_INET6;
594 #if 0 /* symbol "in6addr_any" is not included in wsock32.lib */
595 /* if wsock32.lib will be linked, we can't refer "in6addr_any" */
596 ((struct sockaddr_in6 FAR *) &ss)->sin6_addr = in6addr_any;
597 #eles
598 memset(&((struct sockaddr_in6 FAR *) &ss)->sin6_addr, 0,
599 sizeof(struct in_addr6));
600 #endif /* 0 */
601 ((struct sockaddr_in6 FAR *) &ss)->sin6_port =
602 htons(find_local_port(pvar));
603 break;
604 default:
605 /* NOT REACHED */
606 break;
607 }
608
609 bind(s, (struct sockaddr FAR *) &ss, len);
610 }
611 #else
612 if (pvar->socket == INVALID_SOCKET) {
613 struct sockaddr_in addr;
614
615 pvar->socket = s;
616
617 addr.sin_family = AF_INET;
618 addr.sin_port = htons(find_local_port(pvar));
619 addr.sin_addr.s_addr = INADDR_ANY;
620 memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
621
622 bind(s, (struct sockaddr FAR *) &addr, sizeof(addr));
623 }
624 #endif /* INET6 */
625
626 return (pvar->Pconnect) (s, name, namelen);
627 }
628
629 static int PASCAL FAR TTXWSAAsyncSelect(SOCKET s, HWND hWnd, u_int wMsg,
630 long lEvent)
631 {
632 GET_VAR();
633
634 if (s == pvar->socket) {
635 pvar->notification_events = lEvent;
636 pvar->notification_msg = wMsg;
637
638 if (pvar->NotificationWindow == NULL) {
639 pvar->NotificationWindow = hWnd;
640 AUTH_advance_to_next_cred(pvar);
641 }
642 }
643
644 return (pvar->PWSAAsyncSelect) (s, hWnd, wMsg, lEvent);
645 }
646
647 static int PASCAL FAR TTXrecv(SOCKET s, char FAR * buf, int len, int flags)
648 {
649 GET_VAR();
650
651 if (s == pvar->socket) {
652 int ret;
653
654 ssh_heartbeat_lock();
655 ret = PKT_recv(pvar, buf, len);
656 ssh_heartbeat_unlock();
657 return (ret);
658
659 } else {
660 return (pvar->Precv) (s, buf, len, flags);
661 }
662 }
663
664 static int PASCAL FAR TTXsend(SOCKET s, char const FAR * buf, int len,
665 int flags)
666 {
667 GET_VAR();
668
669 if (s == pvar->socket) {
670 ssh_heartbeat_lock();
671 SSH_send(pvar, buf, len);
672 ssh_heartbeat_unlock();
673 return len;
674 } else {
675 return (pvar->Psend) (s, buf, len, flags);
676 }
677 }
678
679 void notify_established_secure_connection(PTInstVar pvar)
680 {
681 #ifdef TERATERM32
682 if (SecureIcon == NULL) {
683 SecureIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_SECURETT));
684 }
685
686 if (SecureIcon != NULL) {
687 pvar->OldSmallIcon =
688 (HICON) SendMessage(pvar->NotificationWindow, WM_GETICON,
689 ICON_SMALL, 0);
690 pvar->OldLargeIcon =
691 (HICON) SendMessage(pvar->NotificationWindow, WM_GETICON,
692 ICON_BIG, 0);
693 PostMessage(pvar->NotificationWindow, WM_SETICON, ICON_BIG,
694 (LPARAM) SecureIcon);
695 PostMessage(pvar->NotificationWindow, WM_SETICON, ICON_SMALL,
696 (LPARAM) SecureIcon);
697 }
698 #endif
699
700 notify_verbose_message(pvar, "Entering secure mode",
701 LOG_LEVEL_VERBOSE);
702 }
703
704 void notify_closed_connection(PTInstVar pvar)
705 {
706 SSH_notify_disconnecting(pvar, NULL);
707 AUTH_notify_disconnecting(pvar);
708 HOSTS_notify_disconnecting(pvar);
709
710 PostMessage(pvar->NotificationWindow, WM_USER_COMMNOTIFY,
711 pvar->socket, MAKELPARAM(FD_CLOSE, 0));
712
713 }
714
715 static void add_err_msg(PTInstVar pvar, char FAR * msg)
716 {
717 if (pvar->err_msg != NULL) {
718 char FAR *buf =
719 (char FAR *) malloc(strlen(pvar->err_msg) + 3 + strlen(msg));
720
721 strcpy(buf, pvar->err_msg);
722 strcat(buf, "\n\n");
723 strcat(buf, msg);
724 free(pvar->err_msg);
725 pvar->err_msg = buf;
726 } else {
727 pvar->err_msg = _strdup(msg);
728 }
729 }
730
731 void notify_nonfatal_error(PTInstVar pvar, char FAR * msg)
732 {
733 if (!pvar->showing_err) {
734 PostMessage(pvar->NotificationWindow, WM_COMMAND,
735 ID_SSHASYNCMESSAGEBOX, 0);
736 }
737 if (msg[0] != 0) {
738 notify_verbose_message(pvar, msg, LOG_LEVEL_ERROR);
739 add_err_msg(pvar, msg);
740 }
741 }
742
743 void notify_fatal_error(PTInstVar pvar, char FAR * msg)
744 {
745 if (msg[0] != 0) {
746 notify_verbose_message(pvar, msg, LOG_LEVEL_FATAL);
747 add_err_msg(pvar, msg);
748 }
749
750 if (!pvar->fatal_error) {
751 pvar->fatal_error = TRUE;
752
753 SSH_notify_disconnecting(pvar, msg);
754 AUTH_notify_disconnecting(pvar);
755 HOSTS_notify_disconnecting(pvar);
756
757 PostMessage(pvar->NotificationWindow, WM_USER_COMMNOTIFY,
758 pvar->socket, MAKELPARAM(FD_CLOSE,
759 (pvar->PWSAGetLastError) ()));
760 }
761 }
762
763 void notify_verbose_message(PTInstVar pvar, char FAR * msg, int level)
764 {
765 if (level <= pvar->session_settings.LogLevel) {
766 char buf[1024];
767 int file;
768
769 get_teraterm_dir_relative_name(buf, NUM_ELEM(buf), "TTSSH.LOG");
770 file = _open(buf, _O_RDWR | _O_APPEND | _O_CREAT | _O_TEXT,
771 _S_IREAD | _S_IWRITE);
772
773 if (file >= 0) {
774 _write(file, msg, strlen(msg));
775 _write(file, "\n", 1);
776 _close(file);
777 }
778 }
779 }
780
781 static void PASCAL FAR TTXOpenTCP(TTXSockHooks FAR * hooks)
782 {
783 GET_VAR();
784
785 if (pvar->settings.Enabled) {
786 char buf[1024] = "\n---------------------------------------------------------------------\nInitiating SSH session at ";
787 struct tm FAR *newtime;
788 time_t long_time;
789
790 pvar->session_settings = pvar->settings;
791
792 time(&long_time);
793 newtime = localtime(&long_time);
794 strcat(buf, asctime(newtime));
795 buf[strlen(buf) - 1] = 0;
796 notify_verbose_message(pvar, buf, LOG_LEVEL_VERBOSE);
797
798 FWDUI_load_settings(pvar);
799
800 pvar->cv->TelAutoDetect = FALSE;
801 /* This next line should not be needed because Teraterm's
802 CommLib should find ts->Telnet == 0 ... but we'll do this
803 just to be on the safe side. */
804 pvar->cv->TelFlag = FALSE;
805
806 pvar->Precv = *hooks->Precv;
807 pvar->Psend = *hooks->Psend;
808 pvar->PWSAAsyncSelect = *hooks->PWSAAsyncSelect;
809 pvar->Pconnect = *hooks->Pconnect;
810 pvar->PWSAGetLastError = *hooks->PWSAGetLastError;
811
812 *hooks->Precv = TTXrecv;
813 *hooks->Psend = TTXsend;
814 *hooks->PWSAAsyncSelect = TTXWSAAsyncSelect;
815 *hooks->Pconnect = TTXconnect;
816
817 SSH_open(pvar);
818 HOSTS_open(pvar);
819 FWDUI_open(pvar);
820 }
821 }
822
823 static void PASCAL FAR TTXCloseTCP(TTXSockHooks FAR * hooks)
824 {
825 GET_VAR();
826
827 if (pvar->session_settings.Enabled) {
828 pvar->socket = INVALID_SOCKET;
829
830 notify_verbose_message(pvar, "Terminating SSH session...",
831 LOG_LEVEL_VERBOSE);
832
833 *hooks->Precv = pvar->Precv;
834 *hooks->Psend = pvar->Psend;
835 *hooks->PWSAAsyncSelect = pvar->PWSAAsyncSelect;
836 *hooks->Pconnect = pvar->Pconnect;
837 }
838
839 uninit_TTSSH(pvar);
840 init_TTSSH(pvar);
841 }
842
843 static void enable_dlg_items(HWND dlg, int from, int to, BOOL enabled)
844 {
845 for (; from <= to; from++) {
846 EnableWindow(GetDlgItem(dlg, from), enabled);
847 }
848 }
849
850 static BOOL CALLBACK TTXHostDlg(HWND dlg, UINT msg, WPARAM wParam,
851 LPARAM lParam)
852 {
853 static char *ssh_version[] = {"SSH1", "SSH2", NULL};
854 PGetHNRec GetHNRec;
855 char EntName[7];
856 char TempHost[HostNameMaxLength + 1];
857 WORD i, j, w;
858 BOOL Ok;
859
860 GET_VAR();
861
862 switch (msg) {
863 case WM_INITDIALOG:
864 GetHNRec = (PGetHNRec) lParam;
865 SetWindowLong(dlg, DWL_USER, lParam);
866
867 if (GetHNRec->PortType == IdFile)
868 GetHNRec->PortType = IdTCPIP;
869 CheckRadioButton(dlg, IDC_HOSTTCPIP, IDC_HOSTSERIAL,
870 IDC_HOSTTCPIP + GetHNRec->PortType - 1);
871
872 strcpy(EntName, "Host");
873
874 i = 1;
875 do {
876 sprintf(&EntName[4], "%d", i);
877 GetPrivateProfileString("Hosts", EntName, "",
878 TempHost, sizeof(TempHost),
879 GetHNRec->SetupFN);
880 if (strlen(TempHost) > 0)
881 SendDlgItemMessage(dlg, IDC_HOSTNAME, CB_ADDSTRING,
882 0, (LPARAM) TempHost);
883 i++;
884 } while ((i <= 99) && (strlen(TempHost) > 0));
885
886 SendDlgItemMessage(dlg, IDC_HOSTNAME, EM_LIMITTEXT,
887 HostNameMaxLength - 1, 0);
888
889 SendDlgItemMessage(dlg, IDC_HOSTNAME, CB_SETCURSEL, 0, 0);
890
891 CheckRadioButton(dlg, IDC_HOSTTELNET, IDC_HOSTOTHER,
892 pvar->settings.Enabled ? IDC_HOSTSSH : GetHNRec->
893 Telnet ? IDC_HOSTTELNET : IDC_HOSTOTHER);
894 SendDlgItemMessage(dlg, IDC_HOSTTCPPORT, EM_LIMITTEXT, 5, 0);
895 SetDlgItemInt(dlg, IDC_HOSTTCPPORT, GetHNRec->TCPPort, FALSE);
896 #ifdef INET6
897 for (i = 0; ProtocolFamilyList[i]; ++i) {
898 SendDlgItemMessage(dlg, IDC_HOSTTCPPROTOCOL, CB_ADDSTRING,
899 0, (LPARAM) ProtocolFamilyList[i]);
900 }
901 SendDlgItemMessage(dlg, IDC_HOSTTCPPROTOCOL, EM_LIMITTEXT,
902 ProtocolFamilyMaxLength - 1, 0);
903 SendDlgItemMessage(dlg, IDC_HOSTTCPPROTOCOL, CB_SETCURSEL, 0, 0);
904 #endif /* INET6 */
905
906 /////// SSH version
907 for (i = 0; ssh_version[i]; ++i) {
908 SendDlgItemMessage(dlg, IDC_SSH_VERSION, CB_ADDSTRING,
909 0, (LPARAM) ssh_version[i]);
910 }
911 SendDlgItemMessage(dlg, IDC_SSH_VERSION, EM_LIMITTEXT,
912 NUM_ELEM(ssh_version) - 1, 0);
913
914 if (pvar->settings.ssh_protocol_version == 1) {
915 SendDlgItemMessage(dlg, IDC_SSH_VERSION, CB_SETCURSEL, 0, 0); // SSH1
916 } else {
917 SendDlgItemMessage(dlg, IDC_SSH_VERSION, CB_SETCURSEL, 1, 0); // SSH2
918 }
919
920 if (IsDlgButtonChecked(dlg, IDC_HOSTSSH)) {
921 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, TRUE); // enabled
922 } else {
923 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, FALSE); // disabled
924 }
925 /////// SSH version
926
927
928 j = 0;
929 w = 1;
930 strcpy(EntName, "COM");
931 for (i = 1; i <= GetHNRec->MaxComPort; i++) {
932 sprintf(&EntName[3], "%d", i);
933 SendDlgItemMessage(dlg, IDC_HOSTCOM, CB_ADDSTRING,
934 0, (LPARAM) EntName);
935 j++;
936 if (GetHNRec->ComPort == i)
937 w = j;
938 }
939 if (j > 0)
940 SendDlgItemMessage(dlg, IDC_HOSTCOM, CB_SETCURSEL, w - 1, 0);
941 else /* All com ports are already used */
942 GetHNRec->PortType = IdTCPIP;
943
944 if (GetHNRec->PortType == IdTCPIP) {
945 enable_dlg_items(dlg, IDC_HOSTCOMLABEL, IDC_HOSTCOM, FALSE);
946
947 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, TRUE);
948 enable_dlg_items(dlg, IDC_SSH_VERSION_LABEL, IDC_SSH_VERSION_LABEL, TRUE);
949 }
950 #ifdef INET6
951 else {
952 enable_dlg_items(dlg, IDC_HOSTNAMELABEL, IDC_HOSTTCPPORT,
953 FALSE);
954 enable_dlg_items(dlg, IDC_HOSTTCPPROTOCOLLABEL,
955 IDC_HOSTTCPPROTOCOL, FALSE);
956
957 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, FALSE); // disabled
958 enable_dlg_items(dlg, IDC_SSH_VERSION_LABEL, IDC_SSH_VERSION_LABEL, FALSE); // disabled (2004.11.23 yutaka)
959 }
960 #else
961 else
962 enable_dlg_items(dlg, IDC_HOSTNAMELABEL, IDC_HOSTTCPPORT,
963 FALSE);
964 #endif /* INET6 */
965
966 // Host dialog���t�H�[�J�X�������� (2004.10.2 yutaka)
967 if (GetHNRec->PortType == IdTCPIP) {
968 HWND hwnd = GetDlgItem(dlg, IDC_HOSTNAME);
969 SetFocus(hwnd);
970 } else {
971 HWND hwnd = GetDlgItem(dlg, IDC_HOSTCOM);
972 SetFocus(hwnd);
973 }
974
975 // SetFocus()���t�H�[�J�X���������������AFALSE�������K�v�������B
976 // TRUE���������ATABSTOP�������������������R���g���[�����I�������B
977 // (2004.11.23 yutaka)
978 return FALSE;
979 //return TRUE;
980
981 case WM_COMMAND:
982 switch (LOWORD(wParam)) {
983 case IDOK:
984 GetHNRec = (PGetHNRec) GetWindowLong(dlg, DWL_USER);
985 if (GetHNRec != NULL) {
986 if (IsDlgButtonChecked(dlg, IDC_HOSTTCPIP)) {
987 #ifdef INET6
988 char afstr[BUFSIZ];
989 #endif /* INET6 */
990 i = GetDlgItemInt(dlg, IDC_HOSTTCPPORT, &Ok, FALSE);
991 if (Ok) {
992 GetHNRec->TCPPort = i;
993 } else {
994 MessageBox(dlg, "Teraterm",
995 "The TCP port must be a number.",
996 MB_OK | MB_ICONEXCLAMATION);
997 return TRUE;
998 }
999 #ifdef INET6
1000 #define getaf(str) \
1001 ((strcmp((str), "IPv6") == 0) ? AF_INET6 : \
1002 ((strcmp((str), "IPv4") == 0) ? AF_INET : AF_UNSPEC))
1003 memset(afstr, 0, sizeof(afstr));
1004 GetDlgItemText(dlg, IDC_HOSTTCPPROTOCOL, afstr,
1005 sizeof(afstr));
1006 GetHNRec->ProtocolFamily = getaf(afstr);
1007 #endif /* INET6 */
1008 GetHNRec->PortType = IdTCPIP;
1009 GetDlgItemText(dlg, IDC_HOSTNAME, GetHNRec->HostName,
1010 HostNameMaxLength);
1011 GetHNRec->Telnet = FALSE;
1012 pvar->hostdlg_activated = TRUE;
1013 pvar->hostdlg_Enabled = FALSE;
1014 if (IsDlgButtonChecked(dlg, IDC_HOSTTELNET)) {
1015 GetHNRec->Telnet = TRUE;
1016 } else if (IsDlgButtonChecked(dlg, IDC_HOSTSSH)) {
1017 pvar->hostdlg_Enabled = TRUE;
1018
1019 // check SSH protocol version
1020 memset(afstr, 0, sizeof(afstr));
1021 GetDlgItemText(dlg, IDC_SSH_VERSION, afstr, sizeof(afstr));
1022 if (stricmp(afstr, "SSH1") == 0) {
1023 pvar->settings.ssh_protocol_version = 1;
1024 } else {
1025 pvar->settings.ssh_protocol_version = 2;
1026 }
1027 }
1028 } else {
1029 GetHNRec->PortType = IdSerial;
1030 GetHNRec->HostName[0] = 0;
1031 memset(EntName, 0, sizeof(EntName));
1032 GetDlgItemText(dlg, IDC_HOSTCOM, EntName,
1033 sizeof(EntName) - 1);
1034 GetHNRec->ComPort = (BYTE) (EntName[3]) - 0x30;
1035 if (strlen(EntName) > 4)
1036 GetHNRec->ComPort =
1037 GetHNRec->ComPort * 10 + (BYTE) (EntName[4]) -
1038 0x30;
1039 }
1040 }
1041 EndDialog(dlg, 1);
1042 return TRUE;
1043
1044 case IDCANCEL:
1045 EndDialog(dlg, 0);
1046 return TRUE;
1047
1048 case IDC_HOSTTCPIP:
1049 enable_dlg_items(dlg, IDC_HOSTNAMELABEL, IDC_HOSTTCPPORT,
1050 TRUE);
1051 #ifdef INET6
1052 enable_dlg_items(dlg, IDC_HOSTTCPPROTOCOLLABEL,
1053 IDC_HOSTTCPPROTOCOL, TRUE);
1054 #endif /* INET6 */
1055 enable_dlg_items(dlg, IDC_HOSTCOMLABEL, IDC_HOSTCOM, FALSE);
1056
1057 enable_dlg_items(dlg, IDC_SSH_VERSION_LABEL, IDC_SSH_VERSION_LABEL, TRUE); // disabled (2004.11.23 yutaka)
1058 if (IsDlgButtonChecked(dlg, IDC_HOSTSSH)) {
1059 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, TRUE);
1060 } else {
1061 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, FALSE); // disabled
1062 }
1063
1064 return TRUE;
1065
1066 case IDC_HOSTSERIAL:
1067 enable_dlg_items(dlg, IDC_HOSTCOMLABEL, IDC_HOSTCOM, TRUE);
1068 enable_dlg_items(dlg, IDC_HOSTNAMELABEL, IDC_HOSTTCPPORT,
1069 FALSE);
1070 #ifdef INET6
1071 enable_dlg_items(dlg, IDC_HOSTTCPPROTOCOLLABEL,
1072 IDC_HOSTTCPPROTOCOL, FALSE);
1073 #endif /* INET6 */
1074 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, FALSE); // disabled
1075 enable_dlg_items(dlg, IDC_SSH_VERSION_LABEL, IDC_SSH_VERSION_LABEL, FALSE); // disabled (2004.11.23 yutaka)
1076
1077 return TRUE;
1078
1079 case IDC_HOSTSSH:
1080 enable_dlg_items(dlg, IDC_SSH_VERSION,
1081 IDC_SSH_VERSION, TRUE);
1082 goto hostssh_enabled;
1083
1084 case IDC_HOSTTELNET:
1085 case IDC_HOSTOTHER:
1086 enable_dlg_items(dlg, IDC_SSH_VERSION, IDC_SSH_VERSION, FALSE); // disabled
1087 hostssh_enabled:
1088
1089 GetHNRec = (PGetHNRec) GetWindowLong(dlg, DWL_USER);
1090
1091 if (IsDlgButtonChecked(dlg, IDC_HOSTTELNET)) {
1092 if (GetHNRec != NULL)
1093 SetDlgItemInt(dlg, IDC_HOSTTCPPORT, GetHNRec->TelPort,
1094 FALSE);
1095 } else if (IsDlgButtonChecked(dlg, IDC_HOSTSSH)) {
1096 SetDlgItemInt(dlg, IDC_HOSTTCPPORT, 22, FALSE);
1097 }
1098 return TRUE;
1099
1100 case IDC_HOSTHELP:
1101 PostMessage(GetParent(dlg), WM_USER_DLGHELP2, 0, 0);
1102 }
1103 }
1104 return FALSE;
1105 }
1106
1107 static BOOL FAR PASCAL TTXGetHostName(HWND parent, PGetHNRec rec)
1108 {
1109 return (BOOL) DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_HOSTDLG),
1110 parent, TTXHostDlg, (LONG) rec);
1111 }
1112
1113 static void PASCAL FAR TTXGetUIHooks(TTXUIHooks FAR * hooks)
1114 {
1115 GET_VAR();
1116
1117 *hooks->GetHostName = TTXGetHostName;
1118 }
1119
1120 static void FAR PASCAL TTXReadINIFile(PCHAR fileName, PTTSet ts)
1121 {
1122 GET_VAR();
1123
1124 (pvar->ReadIniFile) (fileName, ts);
1125 read_ssh_options(pvar, fileName);
1126 pvar->settings = *pvar->ts_SSH;
1127 notify_verbose_message(pvar, "Reading INI file", LOG_LEVEL_VERBOSE);
1128 FWDUI_load_settings(pvar);
1129 }
1130
1131 static void FAR PASCAL TTXWriteINIFile(PCHAR fileName, PTTSet ts)
1132 {
1133 GET_VAR();
1134
1135 (pvar->WriteIniFile) (fileName, ts);
1136 *pvar->ts_SSH = pvar->settings;
1137 clear_local_settings(pvar);
1138 notify_verbose_message(pvar, "Writing INI file", LOG_LEVEL_VERBOSE);
1139 write_ssh_options(pvar, fileName, pvar->ts_SSH);
1140 }
1141
1142 static void read_ssh_options_from_user_file(PTInstVar pvar,
1143 char FAR * user_file_name)
1144 {
1145 if (user_file_name[0] == '.') {
1146 read_ssh_options(pvar, user_file_name);
1147 } else {
1148 char buf[1024];
1149
1150 get_teraterm_dir_relative_name(buf, sizeof(buf), user_file_name);
1151 read_ssh_options(pvar, buf);
1152 }
1153
1154 pvar->settings = *pvar->ts_SSH;
1155 FWDUI_load_settings(pvar);
1156 }
1157
1158
1159 // @���u�����N���u�������B (2005.1.26 yutaka)
1160 static void replace_to_blank(char *src, char *dst, int dst_len)
1161 {
1162 int len, i;
1163
1164 len = strlen(src);
1165 if (dst_len < len) // buffer overflow check
1166 return;
1167
1168 for (i = 0 ; i < len ; i++) {
1169 if (src[i] == '@') { // @ ���o��������
1170 if (i < len - 1 && src[i + 1] == '@') { // �������� @ �����A�b�g�}�[�N���F������
1171 *dst++ = '@';
1172 i++;
1173 } else {
1174 *dst++ = ' '; // �������u��������
1175 }
1176 } else {
1177 *dst++ = src[i];
1178 }
1179 }
1180 *dst = '\0';
1181 }
1182
1183 /* returns 1 if the option text must be deleted */
1184 static int parse_option(PTInstVar pvar, char FAR * option)
1185 {
1186 if ((option[0] == '-' || option[0] == '/')) {
1187 if (MATCH_STR(option + 1, "ssh") == 0) {
1188 if (option[4] == 0) {
1189 pvar->settings.Enabled = 1;
1190 } else if (MATCH_STR(option + 4, "-L") == 0
1191 || MATCH_STR(option + 4, "-R") == 0
1192 || stricmp(option + 4, "-X") == 0) {
1193 if (pvar->settings.DefaultForwarding[0] == 0) {
1194 strcpy(pvar->settings.DefaultForwarding, option + 5);
1195 } else {
1196 strcat(pvar->settings.DefaultForwarding, ";");
1197 strcat(pvar->settings.DefaultForwarding, option + 5);
1198 }
1199 } else if (MATCH_STR(option + 4, "-f=") == 0) {
1200 read_ssh_options_from_user_file(pvar, option + 7);
1201 } else if (MATCH_STR(option + 4, "-v") == 0) {
1202 pvar->settings.LogLevel = LOG_LEVEL_VERBOSE;
1203 } else if (stricmp(option + 4, "-autologin") == 0
1204 || stricmp(option + 4, "-autologon") == 0) {
1205 pvar->settings.TryDefaultAuth = TRUE;
1206
1207 } else if (MATCH_STR(option + 4, "-consume=") == 0) {
1208 read_ssh_options_from_user_file(pvar, option + 13);
1209 DeleteFile(option + 13);
1210 } else {
1211 char buf[1024];
1212
1213 _snprintf(buf, sizeof(buf),
1214 "Unrecognized command-line option: %s", option);
1215 buf[sizeof(buf) - 1] = 0;
1216
1217 MessageBox(NULL, buf, "TTSSH", MB_OK | MB_ICONEXCLAMATION);
1218 }
1219
1220 return 1;
1221 } else if (MATCH_STR(option + 1, "t=") == 0) {
1222 if (strcmp(option + 3, "2") == 0) {
1223 pvar->settings.Enabled = 1;
1224 return 1;
1225 } else {
1226 pvar->settings.Enabled = 0;
1227 }
1228 } else if (MATCH_STR(option + 1, "f=") == 0) {
1229 read_ssh_options_from_user_file(pvar, option + 3);
1230
1231 // /1 ������ /2 �I�v�V�������V�K���� (2004.10.3 yutaka)
1232 } else if (MATCH_STR(option + 1, "1") == 0) {
1233 // command line: /ssh /1 is SSH1 only
1234 pvar->settings.ssh_protocol_version = 1;
1235
1236 } else if (MATCH_STR(option + 1, "2") == 0) {
1237 // command line: /ssh /2 is SSH2 & SSH1
1238 pvar->settings.ssh_protocol_version = 2;
1239
1240 } else if (MATCH_STR(option + 1, "nossh") == 0) {
1241 // '/nossh' �I�v�V�����������B
1242 // TERATERM.INI ��SSH���L�������������������A������Cygterm���N��������������
1243 // �����������������B(2004.10.11 yutaka)
1244 pvar->settings.Enabled = 0;
1245
1246 } else if (MATCH_STR(option + 1, "auth") == 0) {
1247 // SSH2�������O�C���I�v�V����������
1248 //
1249 // SYNOPSIS: /ssh /auth=passowrd /user=���[�U�� /passwd=�p�X���[�h
1250 // /ssh /auth=publickey /user=���[�U�� /passwd=�p�X���[�h /keyfile=�p�X
1251 // EXAMPLE: /ssh /auth=password /user=nike /passwd=a@bc
1252 // /ssh /auth=publickey /user=foo /passwd=bar /keyfile=d:\tmp\id_rsa
1253 // NOTICE: �p�X���[�h���p�X�������������������A�u�����N���������� @ ���g�������B
1254 //
1255 // (2004.11.30 yutaka)
1256 // (2005.1.26 yutaka) ���������B���J���F���T�|�[�g�B
1257 //
1258 pvar->ssh2_autologin = 1; // for SSH2 (2004.11.30 yutaka)
1259
1260 if (MATCH_STR(option + 5, "=password") == 0) { // �p�X���[�h/keyboard-interactive�F��
1261 //pvar->auth_state.cur_cred.method = SSH_AUTH_PASSWORD;
1262 pvar->ssh2_authmethod = SSH_AUTH_PASSWORD;
1263
1264 } else if (MATCH_STR(option + 5, "=publickey") == 0) { // ���J���F��
1265 //pvar->auth_state.cur_cred.method = SSH_AUTH_RSA;
1266 pvar->ssh2_authmethod = SSH_AUTH_RSA;
1267
1268 } else {
1269 // TODO:
1270
1271 }
1272
1273 } else if (MATCH_STR(option + 1, "user=") == 0) {
1274 replace_to_blank(option + 6, pvar->ssh2_username, sizeof(pvar->ssh2_username));
1275 //_snprintf(pvar->ssh2_username, sizeof(pvar->ssh2_username), "%s", option + 6);
1276
1277 } else if (MATCH_STR(option + 1, "passwd=") == 0) {
1278 replace_to_blank(option + 8, pvar->ssh2_password, sizeof(pvar->ssh2_password));
1279 //_snprintf(pvar->ssh2_password, sizeof(pvar->ssh2_password), "%s", option + 8);
1280
1281 } else if (MATCH_STR(option + 1, "keyfile=") == 0) {
1282 replace_to_blank(option + 9, pvar->ssh2_keyfile, sizeof(pvar->ssh2_keyfile));
1283
1284 }
1285
1286 }
1287
1288 return 0;
1289 }
1290
1291 static void FAR PASCAL TTXParseParam(PCHAR param, PTTSet ts,
1292 PCHAR DDETopic)
1293 {
1294 int i;
1295 BOOL inParam = FALSE;
1296 BOOL inQuotes = FALSE;
1297 PCHAR option = NULL;
1298 GET_VAR();
1299
1300 if (pvar->hostdlg_activated) {
1301 pvar->settings.Enabled = pvar->hostdlg_Enabled;
1302 }
1303
1304 for (i = 0; param[i] != 0; i++) {
1305 if (inQuotes ? param[i] ==
1306 '"' : (param[i] == ' ' || param[i] == '\t')) {
1307 if (option != NULL) {
1308 char ch = param[i];
1309
1310 param[i] = 0;
1311 if (parse_option
1312 (pvar, *option == '"' ? option + 1 : option)) {
1313 memset(option, ' ', i + 1 - (option - param));
1314 } else {
1315 param[i] = ch;
1316 }
1317 option = NULL;
1318 }
1319 inParam = FALSE;
1320 inQuotes = FALSE;
1321 } else if (!inParam) {
1322 if (param[i] == '"') {
1323 inQuotes = TRUE;
1324 inParam = TRUE;
1325 option = param + i;
1326 } else if (param[i] != ' ' && param[i] != '\t') {
1327 inParam = TRUE;
1328 option = param + i;
1329 }
1330 }
1331 }
1332
1333 if (option != NULL) {
1334 if (parse_option(pvar, option)) {
1335 memset(option, ' ', i - (option - param));
1336 }
1337 }
1338
1339 FWDUI_load_settings(pvar);
1340
1341 (pvar->ParseParam) (param, ts, DDETopic);
1342
1343 }
1344
1345 static void PASCAL FAR TTXGetSetupHooks(TTXSetupHooks FAR * hooks)
1346 {
1347 GET_VAR();
1348
1349 pvar->ReadIniFile = *hooks->ReadIniFile;
1350 pvar->WriteIniFile = *hooks->WriteIniFile;
1351 pvar->ParseParam = *hooks->ParseParam;
1352
1353 *hooks->ReadIniFile = TTXReadINIFile;
1354 *hooks->WriteIniFile = TTXWriteINIFile;
1355 *hooks->ParseParam = TTXParseParam;
1356 }
1357
1358 static void PASCAL FAR TTXSetWinSize(int rows, int cols)
1359 {
1360 GET_VAR();
1361
1362 SSH_notify_win_size(pvar, cols, rows);
1363 }
1364
1365 static void insertMenuBeforeItem(HMENU menu, WORD beforeItemID, WORD flags,
1366 WORD newItemID, char FAR * text)
1367 {
1368 int i, j;
1369
1370 for (i = GetMenuItemCount(menu) - 1; i >= 0; i--) {
1371 HMENU submenu = GetSubMenu(menu, i);
1372
1373 for (j = GetMenuItemCount(submenu) - 1; j >= 0; j--) {
1374 if (GetMenuItemID(submenu, j) == beforeItemID) {
1375 InsertMenu(submenu, j, MF_BYPOSITION | flags, newItemID,
1376 text);
1377 return;
1378 }
1379 }
1380 }
1381 }
1382
1383 static void PASCAL FAR TTXModifyMenu(HMENU menu)
1384 {
1385 GET_VAR();
1386
1387 /* inserts before ID_HELP_ABOUT */
1388 insertMenuBeforeItem(menu, 50990, MF_ENABLED, ID_ABOUTMENU,
1389 "About &TTSSH...");
1390
1391 /* inserts before ID_SETUP_TCPIP */
1392 insertMenuBeforeItem(menu, 50360, MF_ENABLED, ID_SSHSETUPMENU,
1393 "SS&H...");
1394 /* inserts before ID_SETUP_TCPIP */
1395 insertMenuBeforeItem(menu, 50360, MF_ENABLED, ID_SSHAUTHSETUPMENU,
1396 "SSH &Authentication...");
1397 /* inserts before ID_SETUP_TCPIP */
1398 insertMenuBeforeItem(menu, 50360, MF_ENABLED, ID_SSHFWDSETUPMENU,
1399 "SSH F&orwarding...");
1400
1401 insertMenuBeforeItem(menu, 50360, MF_ENABLED, ID_SSHKEYGENMENU,
1402 "SSH KeyGenerator...");
1403 }
1404
1405 static void append_about_text(HWND dlg, char FAR * prefix, char FAR * msg)
1406 {
1407 SendDlgItemMessage(dlg, IDC_ABOUTTEXT, EM_REPLACESEL, 0,
1408 (LPARAM) prefix);
1409 SendDlgItemMessage(dlg, IDC_ABOUTTEXT, EM_REPLACESEL, 0, (LPARAM) msg);
1410 SendDlgItemMessage(dlg, IDC_ABOUTTEXT, EM_REPLACESEL, 0,
1411 (LPARAM) (char FAR *) "\r\n");
1412 }
1413
1414 // ���s�t�@�C�������o�[�W�������������� (2005.2.28 yutaka)
1415 void get_file_version(char *exefile, int *major, int *minor, int *release, int *build)
1416 {
1417 typedef struct {
1418 WORD wLanguage;
1419 WORD wCodePage;
1420 } LANGANDCODEPAGE, *LPLANGANDCODEPAGE;
1421 LPLANGANDCODEPAGE lplgcode;
1422 UINT unLen;
1423 DWORD size;
1424 char *buf = NULL;
1425 BOOL ret;
1426 int i;
1427 char fmt[80];
1428 char *pbuf;
1429
1430 size = GetFileVersionInfoSize(exefile, NULL);
1431 if (size == 0) {
1432 goto error;
1433 }
1434 buf = malloc(size);
1435 ZeroMemory(buf, size);
1436
1437 if (GetFileVersionInfo(exefile, 0, size, buf) == FALSE) {
1438 goto error;
1439 }
1440
1441 ret = VerQueryValue(buf,
1442 "\\VarFileInfo\\Translation",
1443 (LPVOID *)&lplgcode, &unLen);
1444 if (ret == FALSE)
1445 goto error;
1446
1447 for (i = 0 ; i < (int)(unLen / sizeof(LANGANDCODEPAGE)) ; i++) {
1448 _snprintf(fmt, sizeof(fmt), "\\StringFileInfo\\%04x%04x\\FileVersion",
1449 lplgcode[i].wLanguage, lplgcode[i].wCodePage);
1450 VerQueryValue(buf, fmt, &pbuf, &unLen);
1451 if (unLen > 0) { // get success
1452 int n, a, b, c, d;
1453
1454 n = sscanf(pbuf, "%d, %d, %d, %d", &a, &b, &c, &d);
1455 if (n == 4) { // convert success
1456 *major = a;
1457 *minor = b;
1458 *release = c;
1459 *build = d;
1460 break;
1461 }
1462 }
1463 }
1464
1465 free(buf);
1466 return;
1467
1468 error:
1469 free(buf);
1470 *major = *minor = *release = *build = 0;
1471 }
1472
1473 static void init_about_dlg(PTInstVar pvar, HWND dlg)
1474 {
1475 char buf[1024];
1476 int a, b, c, d;
1477
1478 // TTSSH���o�[�W�������������� (2005.2.28 yutaka)
1479 get_file_version("ttxssh.dll", &a, &b, &c, &d);
1480 _snprintf(buf, sizeof(buf), "TTSSH\r\nTeraterm Secure Shell extension, %d.%d", a, b);
1481 SendMessage(GetDlgItem(dlg, IDC_TTSSH_VERSION), WM_SETTEXT, 0, (LPARAM)buf);
1482
1483 // OpenSSL���o�[�W�������������� (2005.1.24 yutaka)
1484 // ���������� (2005.5.11 yutaka)
1485 #ifdef OPENSSL_VERSION_TEXT
1486 SendMessage(GetDlgItem(dlg, IDC_OPENSSL_VERSION), WM_SETTEXT, 0, (LPARAM)OPENSSL_VERSION_TEXT);
1487 #else
1488 SendMessage(GetDlgItem(dlg, IDC_OPENSSL_VERSION), WM_SETTEXT, 0, (LPARAM)"Unknown");
1489 #endif
1490
1491 // zlib���o�[�W�������������� (2005.5.11 yutaka)
1492 #ifdef ZLIB_VERSION
1493 _snprintf(buf, sizeof(buf), "ZLib %s", ZLIB_VERSION);
1494 #else
1495 _snprintf(buf, sizeof(buf), "ZLib Unknown");
1496 #endif
1497 SendMessage(GetDlgItem(dlg, IDC_ZLIB_VERSION), WM_SETTEXT, 0, (LPARAM)buf);
1498
1499
1500 // TTSSH�_�C�A���O���\������SSH������������ (2004.10.30 yutaka)
1501 if (pvar->socket != INVALID_SOCKET) {
1502 if (SSHv1(pvar)) {
1503 SSH_get_server_ID_info(pvar, buf, sizeof(buf));
1504 append_about_text(dlg, "Server ID: ", buf);
1505 SSH_get_protocol_version_info(pvar, buf, sizeof(buf));
1506 append_about_text(dlg, "Using protocol: ", buf);
1507 CRYPT_get_cipher_info(pvar, buf, sizeof(buf));
1508 append_about_text(dlg, "Encryption: ", buf);
1509 CRYPT_get_server_key_info(pvar, buf, sizeof(buf));
1510 append_about_text(dlg, "Server keys: ", buf);
1511 AUTH_get_auth_info(pvar, buf, sizeof(buf));
1512 append_about_text(dlg, "Authentication: ", buf);
1513 SSH_get_compression_info(pvar, buf, sizeof(buf));
1514 append_about_text(dlg, "Compression: ", buf);
1515
1516 } else { // SSH2
1517 SSH_get_server_ID_info(pvar, buf, sizeof(buf));
1518 append_about_text(dlg, "Server ID: ", buf);
1519
1520 append_about_text(dlg, "Client ID: ", pvar->client_version_string);
1521
1522 SSH_get_protocol_version_info(pvar, buf, sizeof(buf));
1523 append_about_text(dlg, "Using protocol: ", buf);
1524
1525 if (pvar->kex_type == KEX_DH_GRP1_SHA1) {
1526 strcpy(buf, KEX_DH1);
1527 } else if (pvar->kex_type == KEX_DH_GRP14_SHA1) {
1528 strcpy(buf, KEX_DH14);
1529 } else {
1530 strcpy(buf, KEX_DHGEX);
1531 }
1532 append_about_text(dlg, "KEX: ", buf);
1533
1534 if (pvar->hostkey_type == KEY_DSA) {
1535 strcpy(buf, "ssh-dss");
1536 } else {
1537 strcpy(buf, "ssh-rsa");
1538 }
1539 append_about_text(dlg, "Host Key: ", buf);
1540
1541 // add HMAC algorithm (2004.12.17 yutaka)
1542 buf[0] = '\0';
1543 if (pvar->ctos_hmac == HMAC_SHA1) {
1544 strcat(buf, "hmac-sha1");
1545 } else if (pvar->ctos_hmac == HMAC_MD5) {
1546 strcat(buf, "hmac-md5");
1547 }
1548 strcat(buf, " to server, ");
1549 if (pvar->stoc_hmac == HMAC_SHA1) {
1550 strcat(buf, "hmac-sha1");
1551 } else if (pvar->stoc_hmac == HMAC_MD5) {
1552 strcat(buf, "hmac-md5");
1553 }
1554 strcat(buf, " from server");
1555 append_about_text(dlg, "HMAC: ", buf);
1556
1557 CRYPT_get_cipher_info(pvar, buf, sizeof(buf));
1558 append_about_text(dlg, "Encryption: ", buf);
1559 CRYPT_get_server_key_info(pvar, buf, sizeof(buf));
1560 append_about_text(dlg, "Server keys: ", buf);
1561 AUTH_get_auth_info(pvar, buf, sizeof(buf));
1562 append_about_text(dlg, "Authentication: ", buf);
1563 SSH_get_compression_info(pvar, buf, sizeof(buf));
1564 append_about_text(dlg, "Compression: ", buf);
1565
1566 }
1567 }
1568 }
1569
1570 static BOOL CALLBACK TTXAboutDlg(HWND dlg, UINT msg, WPARAM wParam,
1571 LPARAM lParam)
1572 {
1573 switch (msg) {
1574 case WM_INITDIALOG:
1575 init_about_dlg((PTInstVar) lParam, dlg);
1576 return TRUE;
1577 case WM_COMMAND:
1578 switch (LOWORD(wParam)) {
1579 case IDOK:
1580 EndDialog(dlg, 1);
1581 return TRUE;
1582 case IDCANCEL: /* there isn't a cancel button, but other Windows
1583 UI things can send this message */
1584 EndDialog(dlg, 0);
1585 return TRUE;
1586 }
1587 break;
1588 }
1589
1590 return FALSE;
1591 }
1592
1593 static char FAR *get_cipher_name(int cipher)
1594 {
1595 switch (cipher) {
1596 case SSH_CIPHER_NONE:
1597 return "<ciphers below this line are disabled>";
1598 case SSH_CIPHER_RC4:
1599 return "RC4";
1600 case SSH_CIPHER_3DES:
1601 return "3DES";
1602 case SSH_CIPHER_DES:
1603 return "DES";
1604 case SSH_CIPHER_IDEA:
1605 return "IDEA";
1606 case SSH_CIPHER_TSS:
1607 return "TSS";
1608 case SSH_CIPHER_BLOWFISH:
1609 return "Blowfish";
1610
1611 // for SSH2(yutaka)
1612 case SSH_CIPHER_AES128:
1613 return "AES128(SSH2)";
1614 case SSH_CIPHER_3DES_CBC:
1615 return "3DES-CBC(SSH2)";
1616
1617 default:
1618 return NULL;
1619 }
1620 }
1621
1622 static void set_move_button_status(HWND dlg)
1623 {
1624 HWND cipherControl = GetDlgItem(dlg, IDC_SSHCIPHERPREFS);
1625 int curPos = (int) SendMessage(cipherControl, LB_GETCURSEL, 0, 0);
1626 int maxPos = (int) SendMessage(cipherControl, LB_GETCOUNT, 0, 0) - 1;
1627
1628 EnableWindow(GetDlgItem(dlg, IDC_SSHMOVECIPHERUP), curPos > 0
1629 && curPos <= maxPos);
1630 EnableWindow(GetDlgItem(dlg, IDC_SSHMOVECIPHERDOWN), curPos >= 0
1631 && curPos < maxPos);
1632 }
1633
1634 static void init_setup_dlg(PTInstVar pvar, HWND dlg)
1635 {
1636 HWND compressionControl = GetDlgItem(dlg, IDC_SSHCOMPRESSIONLEVEL);
1637 HWND cipherControl = GetDlgItem(dlg, IDC_SSHCIPHERPREFS);
1638 int i;
1639 int ch;
1640
1641 SendMessage(compressionControl, TBM_SETRANGE, TRUE, MAKELONG(0, 9));
1642 SendMessage(compressionControl, TBM_SETPOS, TRUE,
1643 pvar->settings.CompressionLevel);
1644
1645 normalize_cipher_order(pvar->settings.CipherOrder);
1646 SSH2_update_cipher_myproposal(pvar); // yutaka
1647
1648 for (i = 0; pvar->settings.CipherOrder[i] != 0; i++) {
1649 int cipher = pvar->settings.CipherOrder[i] - '0';
1650 char FAR *name = get_cipher_name(cipher);
1651
1652 if (name != NULL) {
1653 SendMessage(cipherControl, LB_ADDSTRING, 0, (LPARAM) name);
1654 }
1655 }
1656
1657 SendMessage(cipherControl, LB_SETCURSEL, 0, 0);
1658 set_move_button_status(dlg);
1659
1660 for (i = 0; (ch = pvar->settings.KnownHostsFiles[i]) != 0 && ch != ';';
1661 i++) {
1662 }
1663 if (ch != 0) {
1664 pvar->settings.KnownHostsFiles[i] = 0;
1665 SetDlgItemText(dlg, IDC_READWRITEFILENAME,
1666 pvar->settings.KnownHostsFiles);
1667 pvar->settings.KnownHostsFiles[i] = ch;
1668 SetDlgItemText(dlg, IDC_READONLYFILENAME,
1669 pvar->settings.KnownHostsFiles + i + 1);
1670 } else {
1671 SetDlgItemText(dlg, IDC_READWRITEFILENAME,
1672 pvar->settings.KnownHostsFiles);
1673 }
1674
1675 // SSH2 HeartBeat(keep-alive)������ (2005.2.22 yutaka)
1676 {
1677 char buf[10];
1678 _snprintf(buf, sizeof(buf), "%d", pvar->settings.ssh_heartbeat_overtime);
1679 SetDlgItemText(dlg, IDC_HEARTBEAT_EDIT, buf);
1680 }
1681
1682 }
1683
1684 void get_teraterm_dir_relative_name(char FAR * buf, int bufsize,
1685 char FAR * basename)
1686 {
1687 int filename_start = 0;
1688 int i;
1689 int ch;
1690
1691 if (basename[0] == '\\' || basename[0] == '/'
1692 || (basename[0] != 0 && basename[1] == ':')) {
1693 strncpy(buf, basename, bufsize);
1694 buf[bufsize - 1] = 0;
1695 return;
1696 }
1697
1698 GetModuleFileName(NULL, buf, bufsize);
1699 for (i = 0; (ch = buf[i]) != 0; i++) {
1700 if (ch == '\\' || ch == '/' || ch == ':') {
1701 filename_start = i + 1;
1702 }
1703 }
1704
1705 if (bufsize > filename_start) {
1706 strncpy(buf + filename_start, basename, bufsize - filename_start);
1707 }
1708 buf[bufsize - 1] = 0;
1709 }
1710
1711 int copy_teraterm_dir_relative_path(char FAR * dest, int destsize,
1712 char FAR * basename)
1713 {
1714 char buf[1024];
1715 int filename_start = 0;
1716 int i;
1717 int ch, ch2;
1718
1719 if (basename[0] != '\\' && basename[0] != '/'
1720 && (basename[0] == 0 || basename[1] != ':')) {
1721 strncpy(dest, basename, destsize);
1722 dest[destsize - 1] = 0;
1723 return strlen(dest);
1724 }
1725
1726 GetModuleFileName(NULL, buf, sizeof(buf));
1727 for (i = 0; (ch = buf[i]) != 0; i++) {
1728 if (ch == '\\' || ch == '/' || ch == ':') {
1729 filename_start = i + 1;
1730 }
1731 }
1732
1733 for (i = 0; i < filename_start; i++) {
1734 ch = toupper(buf[i]);
1735 ch2 = toupper(basename[i]);
1736
1737 if (ch == ch2
1738 || ((ch == '\\' || ch == '/')
1739 && (ch2 == '\\' || ch2 == '/'))) {
1740 } else {
1741 break;
1742 }
1743 }
1744
1745 if (i == filename_start) {
1746 strncpy(dest, basename + i, destsize);
1747 } else {
1748 strncpy(dest, basename, destsize);
1749 }
1750 dest[destsize - 1] = 0;
1751 return strlen(dest);
1752 }
1753
1754 static void complete_setup_dlg(PTInstVar pvar, HWND dlg)
1755 {
1756 char buf[4096];
1757 char buf2[1024];
1758 HWND compressionControl = GetDlgItem(dlg, IDC_SSHCOMPRESSIONLEVEL);
1759 HWND cipherControl = GetDlgItem(dlg, IDC_SSHCIPHERPREFS);
1760 int i, j, buf2index, bufindex;
1761 int count = (int) SendMessage(cipherControl, LB_GETCOUNT, 0, 0);
1762
1763 pvar->settings.CompressionLevel =
1764 (int) SendMessage(compressionControl, TBM_GETPOS, 0, 0);
1765
1766 buf2index = 0;
1767 for (i = 0; i < count; i++) {
1768 int len = SendMessage(cipherControl, LB_GETTEXTLEN, i, 0);
1769
1770 if (len > 0 && len < sizeof(buf)) { /* should always be true */
1771 buf[0] = 0;
1772 SendMessage(cipherControl, LB_GETTEXT, i, (LPARAM) buf);
1773 for (j = 0;
1774 j <= SSH_CIPHER_MAX
1775 && strcmp(buf, get_cipher_name(j)) != 0; j++) {
1776 }
1777 if (j <= SSH_CIPHER_MAX) {
1778 buf2[buf2index] = '0' + j;
1779 buf2index++;
1780 }
1781 }
1782 }
1783 buf2[buf2index] = 0;
1784 normalize_cipher_order(buf2);
1785 strcpy(pvar->settings.CipherOrder, buf2);
1786 SSH2_update_cipher_myproposal(pvar); // yutaka
1787
1788 buf[0] = 0;
1789 GetDlgItemText(dlg, IDC_READWRITEFILENAME, buf, sizeof(buf));
1790 j = copy_teraterm_dir_relative_path(pvar->settings.KnownHostsFiles,
1791 sizeof(pvar->settings.
1792 KnownHostsFiles), buf);
1793 buf[0] = 0;
1794 bufindex = 0;
1795 GetDlgItemText(dlg, IDC_READONLYFILENAME, buf, sizeof(buf));
1796 for (i = 0; buf[i] != 0; i++) {
1797 if (buf[i] == ';') {
1798 buf[i] = 0;
1799 if (j < sizeof(pvar->settings.KnownHostsFiles) - 1) {
1800 pvar->settings.KnownHostsFiles[j] = ';';
1801 j++;
1802 j += copy_teraterm_dir_relative_path(pvar->settings.
1803 KnownHostsFiles + j,
1804 sizeof(pvar->settings.
1805 KnownHostsFiles)
1806 - j, buf + bufindex);
1807 }
1808 bufindex = i + 1;
1809 }
1810 }
1811 if (bufindex < i && j < sizeof(pvar->settings.KnownHostsFiles) - 1) {
1812 pvar->settings.KnownHostsFiles[j] = ';';
1813 j++;
1814 copy_teraterm_dir_relative_path(pvar->settings.KnownHostsFiles + j,
1815 sizeof(pvar->settings.
1816 KnownHostsFiles) - j,
1817 buf + bufindex);
1818 }
1819
1820 // get SSH HeartBeat(keep-alive)
1821 SendMessage(GetDlgItem(dlg, IDC_HEARTBEAT_EDIT), WM_GETTEXT, sizeof(buf), (LPARAM)buf);
1822 i = atoi(buf);
1823 if (i < 0)
1824 i = 60;
1825 pvar->settings.ssh_heartbeat_overtime = i;
1826
1827 }
1828
1829 static void move_cur_sel_delta(HWND listbox, int delta)
1830 {
1831 int curPos = (int) SendMessage(listbox, LB_GETCURSEL, 0, 0);
1832 int maxPos = (int) SendMessage(listbox, LB_GETCOUNT, 0, 0) - 1;
1833 int newPos = curPos + delta;
1834 char buf[1024];
1835
1836 if (curPos >= 0 && newPos >= 0 && newPos <= maxPos) {
1837 int len = SendMessage(listbox, LB_GETTEXTLEN, curPos, 0);
1838
1839 if (len > 0 && len < sizeof(buf)) { /* should always be true */
1840 buf[0] = 0;
1841 SendMessage(listbox, LB_GETTEXT, curPos, (LPARAM) buf);
1842 SendMessage(listbox, LB_DELETESTRING, curPos, 0);
1843 SendMessage(listbox, LB_INSERTSTRING, newPos,
1844 (LPARAM) (char FAR *) buf);
1845 SendMessage(listbox, LB_SETCURSEL, newPos, 0);
1846 }
1847 }
1848 }
1849
1850 static int get_keys_file_name(HWND parent, char FAR * buf, int bufsize,
1851 int readonly)
1852 {
1853 #ifdef TERATERM32
1854 OPENFILENAME params;
1855 char fullname_buf[2048] = "ssh_known_hosts";
1856
1857 params.lStructSize = sizeof(OPENFILENAME);
1858 params.hwndOwner = parent;
1859 params.lpstrFilter = NULL;
1860 params.lpstrCustomFilter = NULL;
1861 params.nFilterIndex = 0;
1862 buf[0] = 0;
1863 params.lpstrFile = fullname_buf;
1864 params.nMaxFile = sizeof(fullname_buf);
1865 params.lpstrFileTitle = NULL;
1866 params.lpstrInitialDir = NULL;
1867 params.lpstrTitle =
1868 readonly ? "Choose a read-only known-hosts file to add" :
1869 "Choose a read/write known-hosts file";
1870 params.Flags = (readonly ? OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST : 0)
1871 | OFN_HIDEREADONLY | (!readonly ? OFN_NOREADONLYRETURN : 0);
1872 params.lpstrDefExt = NULL;
1873
1874 if (GetOpenFileName(&params) != 0) {
1875 copy_teraterm_dir_relative_path(buf, bufsize, fullname_buf);
1876 return 1;
1877 } else {
1878 int err = CommDlgExtendedError();
1879
1880 if (err != 0) {
1881 char buf[1024];
1882
1883 _snprintf(buf, sizeof(buf),
1884 "Cannot show file dialog box: error %d", err);
1885 buf[sizeof(buf) - 1] = 0;
1886 MessageBox(parent, buf, "TTSSH Error",
1887 MB_OK | MB_ICONEXCLAMATION);
1888 }
1889
1890 return 0;
1891 }
1892 #else
1893 return 0;
1894 #endif
1895 }
1896
1897 static void choose_read_write_file(HWND dlg)
1898 {
1899 char buf[1024];
1900
1901 if (get_keys_file_name(dlg, buf, sizeof(buf), 0)) {
1902 SetDlgItemText(dlg, IDC_READWRITEFILENAME, buf);
1903 }
1904 }
1905
1906 static void choose_read_only_file(HWND dlg)
1907 {
1908 char buf[1024];
1909 char buf2[4096];
1910
1911 if (get_keys_file_name(dlg, buf, sizeof(buf), 1)) {
1912 buf2[0] = 0;
1913 GetDlgItemText(dlg, IDC_READONLYFILENAME, buf2, sizeof(buf2));
1914 if (buf2[0] != 0 && buf2[strlen(buf2) - 1] != ';') {
1915 strncat(buf2, ";", sizeof(buf2));
1916 }
1917 strncat(buf2, buf, sizeof(buf2));
1918 SetDlgItemText(dlg, IDC_READONLYFILENAME, buf2);
1919 }
1920 }
1921
1922 static BOOL CALLBACK TTXSetupDlg(HWND dlg, UINT msg, WPARAM wParam,
1923 LPARAM lParam)
1924 {
1925 switch (msg) {
1926 case WM_INITDIALOG:
1927 SetWindowLong(dlg, DWL_USER, lParam);
1928 init_setup_dlg((PTInstVar) lParam, dlg);
1929 return TRUE;
1930 case WM_COMMAND:
1931 switch (LOWORD(wParam)) {
1932 case IDOK:
1933 complete_setup_dlg((PTInstVar) GetWindowLong(dlg, DWL_USER),
1934 dlg);
1935 EndDialog(dlg, 1);
1936 return TRUE;
1937 case IDCANCEL: /* there isn't a cancel button, but other Windows
1938 UI things can send this message */
1939 EndDialog(dlg, 0);
1940 return TRUE;
1941 case IDC_SSHMOVECIPHERUP:
1942 move_cur_sel_delta(GetDlgItem(dlg, IDC_SSHCIPHERPREFS), -1);
1943 set_move_button_status(dlg);
1944 SetFocus(GetDlgItem(dlg, IDC_SSHCIPHERPREFS));
1945 return TRUE;
1946 case IDC_SSHMOVECIPHERDOWN:
1947 move_cur_sel_delta(GetDlgItem(dlg, IDC_SSHCIPHERPREFS), 1);
1948 set_move_button_status(dlg);
1949 SetFocus(GetDlgItem(dlg, IDC_SSHCIPHERPREFS));
1950 return TRUE;
1951 case IDC_SSHCIPHERPREFS:
1952 set_move_button_status(dlg);
1953 return TRUE;
1954 case IDC_CHOOSEREADWRITEFILE:
1955 choose_read_write_file(dlg);
1956 return TRUE;
1957 case IDC_CHOOSEREADONLYFILE:
1958 choose_read_only_file(dlg);
1959 return TRUE;
1960 }
1961 break;
1962 }
1963
1964 return FALSE;
1965 }
1966
1967
1968 //
1969 // SSH key generator dialog (2005.4.10 yutaka)
1970 //
1971
1972 typedef struct {
1973 RSA *rsa;
1974 DSA *dsa;
1975 } ssh_private_key_t;
1976
1977 static ssh_private_key_t private_key = {NULL, NULL};
1978
1979 typedef struct {
1980 RSA *rsa;
1981 DSA *dsa;
1982 } ssh_public_key_t;
1983
1984 static ssh_public_key_t public_key = {NULL, NULL};;
1985
1986 static void free_ssh_key(void)
1987 {
1988 // DSA_free(), RSA_free()��NULL���n�����������������B
1989 DSA_free(private_key.dsa);
1990 private_key.dsa = NULL;
1991 DSA_free(public_key.dsa);
1992 public_key.dsa = NULL;
1993
1994 RSA_free(private_key.rsa);
1995 private_key.rsa = NULL;
1996 RSA_free(public_key.rsa);
1997 public_key.rsa = NULL;
1998 }
1999
2000
2001 static BOOL generate_ssh_key(enum hostkey_type type)
2002 {
2003 int bits = 1024;
2004
2005 // if SSH key already is generated, should free the resource.
2006 free_ssh_key();
2007
2008 if (type == KEY_RSA1 || type == KEY_RSA) {
2009 RSA *priv = NULL;
2010 RSA *pub = NULL;
2011
2012 // private key
2013 priv = RSA_generate_key(bits, 35, NULL, NULL);
2014 if (priv == NULL)
2015 goto error;
2016 private_key.rsa = priv;
2017
2018 // public key
2019 pub = RSA_new();
2020 pub->n = BN_new();
2021 pub->e = BN_new();
2022 if (pub->n == NULL || pub->e == NULL) {
2023 RSA_free(pub);
2024 goto error;
2025 }
2026
2027 BN_copy(pub->n, priv->n);
2028 BN_copy(pub->e, priv->e);
2029 public_key.rsa = pub;
2030
2031 } else if (type == KEY_DSA) {
2032 DSA *priv = NULL;
2033 DSA *pub = NULL;
2034
2035 // private key
2036 priv = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL);
2037 if (priv == NULL)
2038 goto error;
2039 if (!DSA_generate_key(priv)) {
2040 // TODO: free 'priv'?
2041 goto error;
2042 }
2043 private_key.dsa = priv;
2044
2045 // public key
2046 pub = DSA_new();
2047 if (pub == NULL)
2048 goto error;
2049 pub->p = BN_new();
2050 pub->q = BN_new();
2051 pub->g = BN_new();
2052 pub->pub_key = BN_new();
2053 if (pub->p == NULL || pub->q == NULL || pub->g == NULL || pub->pub_key == NULL) {
2054 DSA_free(pub);
2055 goto error;
2056 }
2057
2058 BN_copy(pub->p, priv->p);
2059 BN_copy(pub->q, priv->q);
2060 BN_copy(pub->g, priv->g);
2061 BN_copy(pub->pub_key, priv->pub_key);
2062 public_key.dsa = pub;
2063
2064 } else {
2065 goto error;
2066 }
2067
2068 return TRUE;
2069
2070 error:
2071 free_ssh_key();
2072 return FALSE;
2073 }
2074
2075
2076 //
2077 // RC4
2078 //
2079
2080 /* Size of key to use */
2081 #define SEED_SIZE 20
2082
2083 /* Number of bytes to reseed after */
2084 #define REKEY_BYTES (1 << 24)
2085
2086 static int rc4_ready = 0;
2087 static RC4_KEY rc4;
2088
2089 static void seed_rng(void)
2090 {
2091 if (RAND_status() != 1)
2092 return;
2093 }
2094
2095 static void arc4random_stir(void)
2096 {
2097 unsigned char rand_buf[SEED_SIZE];
2098 int i;
2099
2100 memset(&rc4, 0, sizeof(rc4));
2101 if (RAND_bytes(rand_buf, sizeof(rand_buf)) <= 0) {
2102 //fatal("Couldn't obtain random bytes (error %ld)",
2103 // ERR_get_error());
2104 }
2105 RC4_set_key(&rc4, sizeof(rand_buf), rand_buf);
2106
2107 /*
2108 * Discard early keystream, as per recommendations in:
2109 * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
2110 */
2111 for(i = 0; i <= 256; i += sizeof(rand_buf))
2112 RC4(&rc4, sizeof(rand_buf), rand_buf, rand_buf);
2113
2114 memset(rand_buf, 0, sizeof(rand_buf));
2115
2116 rc4_ready = REKEY_BYTES;
2117 }
2118
2119 static unsigned int arc4random(void)
2120 {
2121 unsigned int r = 0;
2122 static int first_time = 1;
2123
2124 if (rc4_ready <= 0) {
2125 if (first_time) {
2126 seed_rng();
2127 }
2128 first_time = 0;
2129 arc4random_stir();
2130 }
2131
2132 RC4(&rc4, sizeof(r), (unsigned char *)&r, (unsigned char *)&r);
2133
2134 rc4_ready -= sizeof(r);
2135
2136 return(r);
2137 }
2138
2139 //
2140 // SSH1 3DES
2141 //
2142 /*
2143 * This is used by SSH1:
2144 *
2145 * What kind of triple DES are these 2 routines?
2146 *
2147 * Why is there a redundant initialization vector?
2148 *
2149 * If only iv3 was used, then, this would till effect have been
2150 * outer-cbc. However, there is also a private iv1 == iv2 which
2151 * perhaps makes differential analysis easier. On the other hand, the
2152 * private iv1 probably makes the CRC-32 attack ineffective. This is a
2153 * result of that there is no longer any known iv1 to use when
2154 * choosing the X block.
2155 */
2156 struct ssh1_3des_ctx
2157 {
2158 EVP_CIPHER_CTX k1, k2, k3;
2159 };
2160
2161 static int ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv, int enc)
2162 {
2163 struct ssh1_3des_ctx *c;
2164 u_char *k1, *k2, *k3;
2165
2166 if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) {
2167 c = malloc(sizeof(*c));
2168 EVP_CIPHER_CTX_set_app_data(ctx, c);
2169 }
2170 if (key == NULL)
2171 return (1);
2172 if (enc == -1)
2173 enc = ctx->encrypt;
2174 k1 = k2 = k3 = (u_char *) key;
2175 k2 += 8;
2176 if (EVP_CIPHER_CTX_key_length(ctx) >= 16+8) {
2177 if (enc)
2178 k3 += 16;
2179 else
2180 k1 += 16;
2181 }
2182 EVP_CIPHER_CTX_init(&c->k1);
2183 EVP_CIPHER_CTX_init(&c->k2);
2184 EVP_CIPHER_CTX_init(&c->k3);
2185 if (EVP_CipherInit(&c->k1, EVP_des_cbc(), k1, NULL, enc) == 0 ||
2186 EVP_CipherInit(&c->k2, EVP_des_cbc(), k2, NULL, !enc) == 0 ||
2187 EVP_CipherInit(&c->k3, EVP_des_cbc(), k3, NULL, enc) == 0) {
2188 memset(c, 0, sizeof(*c));
2189 free(c);
2190 EVP_CIPHER_CTX_set_app_data(ctx, NULL);
2191 return (0);
2192 }
2193 return (1);
2194 }
2195
2196 static int ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, u_int len)
2197 {
2198 struct ssh1_3des_ctx *c;
2199
2200 if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) {
2201 //error("ssh1_3des_cbc: no context");
2202 return (0);
2203 }
2204 if (EVP_Cipher(&c->k1, dest, (u_char *)src, len) == 0 ||
2205 EVP_Cipher(&c->k2, dest, dest, len) == 0 ||
2206 EVP_Cipher(&c->k3, dest, dest, len) == 0)
2207 return (0);
2208 return (1);
2209 }
2210
2211 static int ssh1_3des_cleanup(EVP_CIPHER_CTX *ctx)
2212 {
2213 struct ssh1_3des_ctx *c;
2214
2215 if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) {
2216 EVP_CIPHER_CTX_cleanup(&c->k1);
2217 EVP_CIPHER_CTX_cleanup(&c->k2);
2218 EVP_CIPHER_CTX_cleanup(&c->k3);
2219 memset(c, 0, sizeof(*c));
2220 free(c);
2221 EVP_CIPHER_CTX_set_app_data(ctx, NULL);
2222 }
2223 return (1);
2224 }
2225
2226 void ssh1_3des_iv(EVP_CIPHER_CTX *evp, int doset, u_char *iv, int len)
2227 {
2228 struct ssh1_3des_ctx *c;
2229
2230 if (len != 24)
2231 //fatal("%s: bad 3des iv length: %d", __func__, len);
2232 ;
2233
2234 if ((c = EVP_CIPHER_CTX_get_app_data(evp)) == NULL)
2235 //fatal("%s: no 3des context", __func__);
2236 ;
2237
2238 if (doset) {
2239 //debug3("%s: Installed 3DES IV", __func__);
2240 memcpy(c->k1.iv, iv, 8);
2241 memcpy(c->k2.iv, iv + 8, 8);
2242 memcpy(c->k3.iv, iv + 16, 8);
2243 } else {
2244 //debug3("%s: Copying 3DES IV", __func__);
2245 memcpy(iv, c->k1.iv, 8);
2246 memcpy(iv + 8, c->k2.iv, 8);
2247 memcpy(iv + 16, c->k3.iv, 8);
2248 }
2249 }
2250
2251 const EVP_CIPHER *evp_ssh1_3des(void)
2252 {
2253 static EVP_CIPHER ssh1_3des;
2254
2255 memset(&ssh1_3des, 0, sizeof(EVP_CIPHER));
2256 ssh1_3des.nid = NID_undef;
2257 ssh1_3des.block_size = 8;
2258 ssh1_3des.iv_len = 0;
2259 ssh1_3des.key_len = 16;
2260 ssh1_3des.init = ssh1_3des_init;
2261 ssh1_3des.cleanup = ssh1_3des_cleanup;
2262 ssh1_3des.do_cipher = ssh1_3des_cbc;
2263 ssh1_3des.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH;
2264 return (&ssh1_3des);
2265 }
2266
2267 static void ssh_make_comment(char *comment, int maxlen)
2268 {
2269 char user[UNLEN + 1], host[128];
2270 DWORD dwSize;
2271 WSADATA wsaData;
2272 int ret;
2273
2274 // get Windows logon user name
2275 dwSize = sizeof(user);
2276 if (GetUserName(user, &dwSize) == 0) {
2277 strcpy(user, "yutaka");
2278 }
2279
2280 // get local hostname (by WinSock)
2281 ret = WSAStartup(MAKEWORD(2,2), &wsaData);
2282 if (ret == 0) {
2283 if (gethostname(host, sizeof(host)) != 0) {
2284 ret = WSAGetLastError();
2285 }
2286 WSACleanup();
2287 }
2288 if (ret != 0) {
2289 strcpy(host, "sai");
2290 }
2291
2292 _snprintf(comment, maxlen, "%s@%s", user, host);
2293 }
2294
2295 // uuencode (rfc1521)
2296 static int uuencode(unsigned char *src, int srclen, unsigned char *target, int targsize)
2297 {
2298 char base64[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2299 char pad = '=';
2300 int datalength = 0;
2301 unsigned char input[3];
2302 unsigned char output[4];
2303 int i;
2304
2305 while (srclen > 2) {
2306 input[0] = *src++;
2307 input[1] = *src++;
2308 input[2] = *src++;
2309 srclen -= 3;
2310
2311 output[0] = input[0] >> 2;
2312 output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
2313 output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
2314 output[3] = input[2] & 0x3f;
2315 if (output[0] >= 64 ||
2316 output[1] >= 64 ||
2317 output[2] >= 64 ||
2318 output[3] >= 64)
2319 return -1;
2320
2321 if (datalength + 4 > targsize)
2322 return (-1);
2323 target[datalength++] = base64[output[0]];
2324 target[datalength++] = base64[output[1]];
2325 target[datalength++] = base64[output[2]];
2326 target[datalength++] = base64[output[3]];
2327 }
2328
2329 if (srclen != 0) {
2330 /* Get what's left. */
2331 input[0] = input[1] = input[2] = '\0';
2332 for (i = 0; i < srclen; i++)
2333 input[i] = *src++;
2334
2335 output[0] = input[0] >> 2;
2336 output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
2337 output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
2338 if (output[0] >= 64 ||
2339 output[1] >= 64 ||
2340 output[2] >= 64)
2341 return -1;
2342
2343 if (datalength + 4 > targsize)
2344 return (-1);
2345 target[datalength++] = base64[output[0]];
2346 target[datalength++] = base64[output[1]];
2347 if (srclen == 1)
2348 target[datalength++] = pad;
2349 else
2350 target[datalength++] = base64[output[2]];
2351 target[datalength++] = pad;
2352 }
2353 if (datalength >= targsize)
2354 return (-1);
2355 target[datalength] = '\0'; /* Returned value doesn't count \0. */
2356
2357 return (datalength); // success
2358 }
2359
2360 static BOOL CALLBACK TTXKeyGenerator(HWND dlg, UINT msg, WPARAM wParam,
2361 LPARAM lParam)
2362 {
2363 static enum hostkey_type key_type;
2364
2365 switch (msg) {
2366 case WM_INITDIALOG:
2367 {
2368 // default key type
2369 SendMessage(GetDlgItem(dlg, IDC_RSA_TYPE), BM_SETCHECK, BST_CHECKED, 0);
2370 key_type = KEY_RSA;
2371
2372 // passphrase edit box disabled(default)
2373 EnableWindow(GetDlgItem(dlg, IDC_KEY_EDIT), FALSE);
2374 EnableWindow(GetDlgItem(dlg, IDC_CONFIRM_EDIT), FALSE);
2375
2376 // file saving dialog disabled(default)
2377 EnableWindow(GetDlgItem(dlg, IDC_SAVE_PUBLIC_KEY), FALSE);
2378 EnableWindow(GetDlgItem(dlg, IDC_SAVE_PRIBATE_KEY), FALSE);
2379
2380 }
2381 return TRUE;
2382
2383 case WM_COMMAND:
2384 switch (LOWORD(wParam)) {
2385 case IDOK: // key generate button pressed
2386 // passphrase edit box disabled(default)
2387 EnableWindow(GetDlgItem(dlg, IDC_KEY_EDIT), FALSE);
2388 EnableWindow(GetDlgItem(dlg, IDC_CONFIRM_EDIT), FALSE);
2389
2390 // file saving dialog disabled(default)
2391 EnableWindow(GetDlgItem(dlg, IDC_SAVE_PUBLIC_KEY), FALSE);
2392 EnableWindow(GetDlgItem(dlg, IDC_SAVE_PRIBATE_KEY), FALSE);
2393
2394 if (generate_ssh_key(key_type)) {
2395 // passphrase edit box disabled(default)
2396 EnableWindow(GetDlgItem(dlg, IDC_KEY_EDIT), TRUE);
2397 EnableWindow(GetDlgItem(dlg, IDC_CONFIRM_EDIT), TRUE);
2398
2399 // file saving dialog disabled(default)
2400 EnableWindow(GetDlgItem(dlg, IDC_SAVE_PUBLIC_KEY), TRUE);
2401 EnableWindow(GetDlgItem(dlg, IDC_SAVE_PRIBATE_KEY), TRUE);
2402 }
2403 return TRUE;
2404
2405 case IDCANCEL:
2406 // don't forget to free SSH resource!
2407 free_ssh_key();
2408 EndDialog(dlg, 0); // dialog close
2409 return TRUE;
2410
2411 // if radio button pressed...
2412 case IDC_RSA1_TYPE | (BN_CLICKED << 16):
2413 key_type = KEY_RSA1;
2414 break;
2415
2416 case IDC_RSA_TYPE | (BN_CLICKED << 16):
2417 key_type = KEY_RSA;
2418 break;
2419
2420 case IDC_DSA_TYPE | (BN_CLICKED << 16):
2421 key_type = KEY_DSA;
2422 break;
2423
2424 // saving public key file
2425 case IDC_SAVE_PUBLIC_KEY:
2426 {
2427 int ret;
2428 OPENFILENAME ofn;
2429 char filename[MAX_PATH];
2430 FILE *fp;
2431 char comment[1024]; // comment string in private key
2432
2433 arc4random_stir();
2434
2435 // saving file dialog
2436 ZeroMemory(&ofn, sizeof(ofn));
2437 ofn.lStructSize = sizeof(ofn);
2438 ofn.hwndOwner = dlg;
2439 if (key_type == KEY_RSA1) {
2440 ofn.lpstrFilter = "SSH1 RSA key(identity.pub)\0identity.pub\0All Files(*.*)\0*.*\0\0";
2441 _snprintf(filename, sizeof(filename), "identity.pub");
2442 } else if (key_type == KEY_RSA) {
2443 ofn.lpstrFilter = "SSH2 RSA key(id_rsa.pub)\0id_rsa.pub\0All Files(*.*)\0*.*\0\0";
2444 _snprintf(filename, sizeof(filename), "id_rsa.pub");
2445 } else {
2446 ofn.lpstrFilter = "SSH2 DSA key(id_dsa.pub)\0id_dsa.pub\0All Files(*.*)\0*.*\0\0";
2447 _snprintf(filename, sizeof(filename), "id_dsa.pub");
2448 }
2449 ofn.lpstrFile = filename;
2450 ofn.nMaxFile = sizeof(filename);
2451 ofn.lpstrTitle = "Save public key as:";
2452 if (GetSaveFileName(&ofn) == 0) { // failure
2453 ret = CommDlgExtendedError();
2454 break;
2455 }
2456
2457 ssh_make_comment(comment, sizeof(comment));
2458
2459 // saving public key file
2460 fp = fopen(filename, "wb");
2461 if (fp == NULL) {
2462 MessageBox(dlg, "Can't open key file", "ERROR", MB_OK | MB_ICONEXCLAMATION);
2463 break;
2464 }
2465
2466 if (key_type == KEY_RSA1) { // SSH1 RSA
2467 RSA *rsa = public_key.rsa;
2468 int bits;
2469 char *buf;
2470
2471 bits = BN_num_bits(rsa->n);
2472 fprintf(fp, "%u", bits);
2473
2474 buf = BN_bn2dec(rsa->e);
2475 fprintf(fp, " %s", buf);
2476 OPENSSL_free(buf);
2477
2478 buf = BN_bn2dec(rsa->n);
2479 fprintf(fp, " %s", buf);
2480 OPENSSL_free(buf);
2481
2482 } else { // SSH2 RSA, DSA
2483 buffer_t *b;
2484 char *keyname;
2485 DSA *dsa = public_key.dsa;
2486 RSA *rsa = public_key.rsa;
2487 int len;
2488 char *blob;
2489 char *uuenc; // uuencode data
2490 int uulen;
2491
2492 b = buffer_init();
2493 if (b == NULL)
2494 goto public_error;
2495
2496 if (key_type == KEY_DSA) { // DSA
2497 keyname = "ssh-dss";
2498 buffer_put_string(b, keyname, strlen(keyname));
2499 buffer_put_bignum2(b, dsa->p);
2500 buffer_put_bignum2(b, dsa->q);
2501 buffer_put_bignum2(b, dsa->g);
2502 buffer_put_bignum2(b, dsa->pub_key);
2503
2504 } else { // RSA
2505 keyname = "ssh-rsa";
2506 buffer_put_string(b, keyname, strlen(keyname));
2507 buffer_put_bignum2(b, rsa->e);
2508 buffer_put_bignum2(b, rsa->n);
2509 }
2510
2511 blob = buffer_ptr(b);
2512 len = buffer_len(b);
2513 uuenc = malloc(len * 2);
2514 if (uuenc == NULL) {
2515 buffer_free(b);
2516 goto public_error;
2517 }
2518 uulen = uuencode(blob, len, uuenc, len * 2);
2519 if (uulen > 0) {
2520 fprintf(fp, "%s %s", keyname, uuenc);
2521 }
2522 free(uuenc);
2523 buffer_free(b);
2524 }
2525
2526 // writing a comment(+LF)
2527 fprintf(fp, " %s", comment);
2528 fputc(0x0a, fp);
2529
2530 public_error:
2531 fclose(fp);
2532
2533 }
2534 break;
2535
2536 // saving private key file
2537 case IDC_SAVE_PRIVATE_KEY:
2538 {
2539 char buf[1024], buf_conf[1024]; // passphrase
2540 int ret;
2541 OPENFILENAME ofn;
2542 char filename[MAX_PATH];
2543 char comment[1024]; // comment string in private key
2544
2545 // �p�X�t���[�Y���`�F�b�N���s���B�p�X�t���[�Y���������t�@�C�����t�����B
2546 SendMessage(GetDlgItem(dlg, IDC_KEY_EDIT), WM_GETTEXT, sizeof(buf), (LPARAM)buf);
2547 SendMessage(GetDlgItem(dlg, IDC_CONFIRM_EDIT), WM_GETTEXT, sizeof(buf_conf), (LPARAM)buf_conf);
2548
2549 // check matching
2550 if (strcmp(buf, buf_conf) != 0) {
2551 MessageBox(dlg, "Two passphrases don't match.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
2552 break;
2553 }
2554
2555 // check empty-passphrase (this is warning level)
2556 if (buf[0] == '\0') {
2557 ret = MessageBox(dlg, "Are you sure that you want to use a empty passphrase?", "WARNING", MB_YESNO | MB_ICONWARNING);
2558 if (ret == IDNO)
2559 break;
2560 }
2561
2562 ssh_make_comment(comment, sizeof(comment));
2563
2564 // saving file dialog
2565 ZeroMemory(&ofn, sizeof(ofn));
2566 ofn.lStructSize = sizeof(ofn);
2567 ofn.hwndOwner = dlg;
2568 if (key_type == KEY_RSA1) {
2569 ofn.lpstrFilter = "SSH1 RSA key(identity)\0identity\0All Files(*.*)\0*.*\0\0";
2570 _snprintf(filename, sizeof(filename), "identity");
2571 } else if (key_type == KEY_RSA) {
2572 ofn.lpstrFilter = "SSH2 RSA key(id_rsa)\0id_rsa\0All Files(*.*)\0*.*\0\0";
2573 _snprintf(filename, sizeof(filename), "id_rsa");
2574 } else {
2575 ofn.lpstrFilter = "SSH2 DSA key(id_dsa)\0id_dsa\0All Files(*.*)\0*.*\0\0";
2576 _snprintf(filename, sizeof(filename), "id_dsa");
2577 }
2578 ofn.lpstrFile = filename;
2579 ofn.nMaxFile = sizeof(filename);
2580 ofn.lpstrTitle = "Save private key as:";
2581 if (GetSaveFileName(&ofn) == 0) { // failure
2582 ret = CommDlgExtendedError();
2583 break;
2584 }
2585
2586 // saving private key file
2587 if (key_type == KEY_RSA1) { // SSH1 RSA
2588 int cipher_num;
2589 buffer_t *b, *enc;
2590 unsigned int rnd;
2591 unsigned char tmp[128];
2592 RSA *rsa;
2593 int i, len;
2594 char authfile_id_string[] = "SSH PRIVATE KEY FILE FORMAT 1.1";
2595 MD5_CTX md;
2596 unsigned char digest[16];
2597 char *passphrase = buf;
2598 EVP_CIPHER_CTX cipher_ctx;
2599 FILE *fp;
2600 char wrapped[4096];
2601
2602 if (passphrase[0] == '\0') { // passphrase is empty
2603 cipher_num = SSH_CIPHER_NONE;
2604 } else {
2605 cipher_num = SSH_CIPHER_3DES; // 3DES-CBC
2606 }
2607
2608 b = buffer_init();
2609 if (b == NULL)
2610 break;
2611 enc = buffer_init();
2612 if (enc == NULL) {
2613 buffer_free(b);
2614 break;
2615 }
2616
2617 // set random value
2618 rnd = arc4random();
2619 tmp[0] = rnd & 0xff;
2620 tmp[1] = (rnd >> 8) & 0xff;
2621 tmp[2] = tmp[0];
2622 tmp[3] = tmp[1];
2623 buffer_append(b, tmp, 4);
2624
2625 // set private key
2626 rsa = private_key.rsa;
2627 buffer_put_bignum(b, rsa->d);
2628 buffer_put_bignum(b, rsa->iqmp);
2629 buffer_put_bignum(b, rsa->q);
2630 buffer_put_bignum(b, rsa->p);
2631
2632 // padding with 8byte align
2633 while (buffer_len(b) % 8) {
2634 buffer_put_char(b, 0);
2635 }
2636
2637 //
2638 // step(2)
2639 //
2640 // encrypted buffer
2641 /* First store keyfile id string. */
2642 for (i = 0 ; authfile_id_string[i] ; i++) {
2643 buffer_put_char(enc, authfile_id_string[i]);
2644 }
2645 buffer_put_char(enc, 0x0a); // LF
2646 buffer_put_char(enc, 0);
2647
2648 /* Store cipher type. */
2649 buffer_put_char(enc, cipher_num);
2650 buffer_put_int(enc, 0); // type is 'int'!! (For future extension)
2651
2652 /* Store public key. This will be in plain text. */
2653 buffer_put_int(enc, BN_num_bits(rsa->n));
2654 buffer_put_bignum(enc, rsa->n);
2655 buffer_put_bignum(enc, rsa->e);
2656 buffer_put_string(enc, comment, strlen(comment));
2657
2658 // setup the MD5ed passphrase to cipher encryption key
2659 MD5_Init(&md);
2660 MD5_Update(&md, (const unsigned char *)passphrase, strlen(passphrase));
2661 MD5_Final(digest, &md);
2662 if (cipher_num == SSH_CIPHER_NONE) {
2663 cipher_init_SSH2(&cipher_ctx, digest, 16, NULL, 0, CIPHER_ENCRYPT, EVP_enc_null);
2664 } else {
2665 cipher_init_SSH2(&cipher_ctx, digest, 16, NULL, 0, CIPHER_ENCRYPT, evp_ssh1_3des);
2666 }
2667 len = buffer_len(b);
2668 if (len % 8) { // fatal error
2669 goto error;
2670 }
2671
2672 // check buffer overflow
2673 if (buffer_overflow_verify(enc, len) && (sizeof(wrapped) < len)) {
2674 goto error;
2675 }
2676
2677 if (EVP_Cipher(&cipher_ctx, wrapped, buffer_ptr(b), len) == 0) {
2678 goto error;
2679 }
2680 if (EVP_CIPHER_CTX_cleanup(&cipher_ctx) == 0) {
2681 goto error;
2682 }
2683
2684 buffer_append(enc, wrapped, len);
2685
2686 // saving private key file (binary mode)
2687 fp = fopen(filename, "wb");
2688 if (fp == NULL) {
2689 MessageBox(dlg, "Can't open key file", "ERROR", MB_OK | MB_ICONEXCLAMATION);
2690 break;
2691 }
2692 fwrite(buffer_ptr(enc), buffer_len(enc), 1, fp);
2693
2694 fclose(fp);
2695
2696 error:;
2697 buffer_free(b);
2698 buffer_free(enc);
2699
2700 } else { // SSH2 RSA, DSA
2701 int len;
2702 FILE *fp;
2703 const EVP_CIPHER *cipher;
2704
2705 len = strlen(buf);
2706 // TODO: range check (len >= 4)
2707
2708 cipher = NULL;
2709 if (len > 0) {
2710 cipher = EVP_des_ede3_cbc();
2711 }
2712
2713 fp = fopen(filename, "w");
2714 if (fp == NULL) {
2715 MessageBox(dlg, "Can't open key file", "ERROR", MB_OK | MB_ICONEXCLAMATION);
2716 break;
2717 }
2718
2719 if (key_type == KEY_RSA) { // RSA
2720 ret = PEM_write_RSAPrivateKey(fp, private_key.rsa, cipher, buf, len, NULL, NULL);
2721 } else { // DSA
2722 ret = PEM_write_DSAPrivateKey(fp, private_key.dsa, cipher, buf, len, NULL, NULL);
2723 }
2724 if (ret == 0) {
2725 MessageBox(dlg, "Can't write key file", "ERROR", MB_OK | MB_ICONEXCLAMATION);
2726 }
2727 fclose(fp);
2728 }
2729
2730
2731 }
2732 break;
2733
2734 }
2735 break;
2736 }
2737
2738 return FALSE;
2739 }
2740
2741
2742 static int PASCAL FAR TTXProcessCommand(HWND hWin, WORD cmd)
2743 {
2744 GET_VAR();
2745
2746 if (pvar->fatal_error) {
2747 return 0;
2748 }
2749
2750 switch (cmd) {
2751 case ID_SSHKEYGENMENU:
2752 if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SSHKEYGEN), hWin, TTXKeyGenerator,
2753 (LPARAM) pvar) == -1) {
2754 MessageBox(hWin, "Cannot create Key Generator window.",
2755 "TTSSH Error", MB_OK | MB_ICONEXCLAMATION);
2756 }
2757 return 1;
2758
2759 case ID_ABOUTMENU:
2760 if (DialogBoxParam
2761 (hInst, MAKEINTRESOURCE(IDD_ABOUTDIALOG), hWin, TTXAboutDlg,
2762 (LPARAM) pvar)
2763 == -1) {
2764 MessageBox(hWin, "Cannot create About box window.",
2765 "TTSSH Error", MB_OK | MB_ICONEXCLAMATION);
2766 }
2767 return 1;
2768 case ID_SSHAUTH:
2769 AUTH_do_cred_dialog(pvar);
2770 return 1;
2771 case ID_SSHSETUPMENU:
2772 if (DialogBoxParam
2773 (hInst, MAKEINTRESOURCE(IDD_SSHSETUP), hWin, TTXSetupDlg,
2774 (LPARAM) pvar)
2775 == -1) {
2776 MessageBox(hWin, "Cannot create TTSSH Setup window.",
2777 "TTSSH Error", MB_OK | MB_ICONEXCLAMATION);
2778 }
2779 return 1;
2780 case ID_SSHAUTHSETUPMENU:
2781 AUTH_do_default_cred_dialog(pvar);
2782 return 1;
2783 case ID_SSHFWDSETUPMENU:
2784 FWDUI_do_forwarding_dialog(pvar);
2785 return 1;
2786 case ID_SSHUNKNOWNHOST:
2787 HOSTS_do_unknown_host_dialog(hWin, pvar);
2788 return 1;
2789 case ID_SSHDIFFERENTHOST:
2790 HOSTS_do_different_host_dialog(hWin, pvar);
2791 return 1;
2792 case ID_SSHASYNCMESSAGEBOX:
2793 if (pvar->err_msg != NULL) {
2794 char FAR *msg = pvar->err_msg;
2795
2796 /* Could there be a buffer overrun bug anywhere in Win32
2797 MessageBox? Who knows? I'm paranoid. */
2798 if (strlen(msg) > 2048) {
2799 msg[2048] = 0;
2800 }
2801
2802 pvar->showing_err = TRUE;
2803 pvar->err_msg = NULL;
2804 #if 1
2805 // XXX: "SECURITY WARINIG" dialog�� ESC �L�[�������������A
2806 // �������A�v���P�[�V�����G���[�����������A���LAPI�������B(2004.12.16 yutaka)
2807 if (!SSHv1(pvar)) {
2808 MessageBox(NULL, msg, "TTSSH",
2809 MB_TASKMODAL | MB_ICONEXCLAMATION);
2810 }
2811 #else
2812 MessageBox(NULL, msg, "TTSSH",
2813 MB_TASKMODAL | MB_ICONEXCLAMATION);
2814 #endif
2815 free(msg);
2816 pvar->showing_err = FALSE;
2817
2818 if (pvar->err_msg != NULL) {
2819 PostMessage(hWin, WM_COMMAND, ID_SSHASYNCMESSAGEBOX, 0);
2820 } else {
2821 AUTH_notify_end_error(pvar);
2822 }
2823 }
2824 return 1;
2825 default:
2826 return 0;
2827 }
2828 }
2829
2830
2831 // ������TeraTerm Menu���R�[�h(ttpmenu.cpp)�������B
2832 // ������ @ ���u���������B@���g��@@�������B(2005.1.28 yutaka)
2833 static void replace_blank_to_mark(char *str, char *dst, int dst_len)
2834 {
2835 int i, len, n;
2836
2837 len = strlen(str);
2838 n = 0;
2839 for (i = 0 ; i < len ; i++) {
2840 if (str[i] == '@')
2841 n++;
2842 }
2843 if (dst_len < (len + 2*n))
2844 return;
2845
2846 for (i = 0 ; i < len ; i++) {
2847 if (str[i] == '@') {
2848 *dst++ = '@';
2849 *dst++ = '@';
2850
2851 } else if (str[i] == ' ') {
2852 *dst++ = '@';
2853
2854 } else {
2855 *dst++ = str[i];
2856
2857 }
2858 }
2859 *dst = '\0';
2860
2861 }
2862
2863 static void PASCAL FAR TTXSetCommandLine(PCHAR cmd, int cmdlen,
2864 PGetHNRec rec)
2865 {
2866 char tmpFile[MAX_PATH];
2867 char tmpPath[1024];
2868 char buf[1024];
2869 int i;
2870 GET_VAR();
2871
2872 GetTempPath(sizeof(tmpPath), tmpPath);
2873 GetTempFileName(tmpPath, "TTX", 0, tmpFile);
2874
2875 for (i = 0; cmd[i] != ' ' && cmd[i] != 0; i++) {
2876 }
2877
2878 if (i < cmdlen) {
2879 strncpy(buf, cmd + i, sizeof(buf));
2880 cmd[i] = 0;
2881
2882 write_ssh_options(pvar, tmpFile, &pvar->settings);
2883
2884 strncat(cmd, " /ssh-consume=", cmdlen);
2885 strncat(cmd, tmpFile, cmdlen);
2886
2887 strncat(cmd, buf, cmdlen);
2888
2889 if (pvar->hostdlg_Enabled) {
2890 strncat(cmd, " /ssh", cmdlen);
2891
2892 // add option of SSH protcol version (2004.10.11 yutaka)
2893 if (pvar->settings.ssh_protocol_version == 2) {
2894 strncat(cmd, " /2", cmdlen);
2895 } else {
2896 strncat(cmd, " /1", cmdlen);
2897 }
2898
2899 }
2900
2901 // �Z�b�V�����������������A�������O�C���p�p�����[�^���t�����B(2005.4.8 yutaka)
2902 if (strstr(buf, "DUPLICATE")) {
2903 char mark[MAX_PATH];
2904 char tmp[MAX_PATH*2];
2905
2906 // �������O�C�������������L�t���O��0�������A�K�v���R�}���h���t�������B
2907 if (!pvar->hostdlg_Enabled) {
2908 _snprintf(tmp, sizeof(tmp), " /ssh /%d", pvar->settings.ssh_protocol_version);
2909 strncat(cmd, tmp, cmdlen);
2910 }
2911
2912 if (pvar->auth_state.cur_cred.method == SSH_AUTH_PASSWORD) {
2913 replace_blank_to_mark(pvar->auth_state.cur_cred.password, mark, sizeof(mark));
2914 _snprintf(tmp, sizeof(tmp), " /auth=password /user=%s /passwd=%s", pvar->auth_state.user, mark);
2915 strncat(cmd, tmp, cmdlen);
2916
2917 } else if (pvar->auth_state.cur_cred.method == SSH_AUTH_RSA) {
2918 replace_blank_to_mark(pvar->auth_state.cur_cred.password, mark, sizeof(mark));
2919 _snprintf(tmp, sizeof(tmp), " /auth=publickey /user=%s /passwd=%s", pvar->auth_state.user, mark);
2920 strncat(cmd, tmp, cmdlen);
2921
2922 replace_blank_to_mark(pvar->session_settings.DefaultRSAPrivateKeyFile, mark, sizeof(mark));
2923 _snprintf(tmp, sizeof(tmp), " /keyfile=%s", mark);
2924 strncat(cmd, tmp, cmdlen);
2925
2926 } else if (pvar->auth_state.cur_cred.method == SSH_AUTH_TIS) {
2927 // keyboard-interactive�F���������������������B
2928
2929 } else {
2930 // don't come here
2931
2932 }
2933
2934 }
2935
2936 }
2937 }
2938
2939 /* This function is called when Teraterm is quitting. You can use it to clean
2940 up.
2941
2942 This function is called for each extension, in reverse load order (see
2943 below).
2944 */
2945 static void PASCAL FAR TTXEnd(void)
2946 {
2947 GET_VAR();
2948
2949 uninit_TTSSH(pvar);
2950
2951 if (pvar->err_msg != NULL) {
2952 /* Could there be a buffer overrun bug anywhere in Win32
2953 MessageBox? Who knows? I'm paranoid. */
2954 if (strlen(pvar->err_msg) > 2048) {
2955 pvar->err_msg[2048] = 0;
2956 }
2957
2958 MessageBox(NULL, pvar->err_msg, "TTSSH",
2959 MB_TASKMODAL | MB_ICONEXCLAMATION);
2960
2961 free(pvar->err_msg);
2962 pvar->err_msg = NULL;
2963 }
2964 #ifndef TERATERM32
2965 DelVar();
2966 #endif
2967 }
2968
2969 /* This record contains all the information that the extension forwards to the
2970 main Teraterm code. It mostly consists of pointers to the above functions.
2971 Any of the function pointers can be replaced with NULL, in which case
2972 Teraterm will just ignore that function and assume default behaviour, which
2973 means "do nothing".
2974 */
2975 static TTXExports Exports = {
2976 /* This must contain the size of the structure. See below for its usage. */
2977 sizeof(TTXExports),
2978 ORDER,
2979
2980 /* Now we just list the functions that we've implemented. */
2981 TTXInit,
2982 TTXGetUIHooks,
2983 TTXGetSetupHooks,
2984 TTXOpenTCP,
2985 TTXCloseTCP,
2986 TTXSetWinSize,
2987 TTXModifyMenu,
2988 NULL,
2989 TTXProcessCommand,
2990 TTXEnd,
2991 TTXSetCommandLine
2992 };
2993
2994 #ifdef TERATERM32
2995 BOOL __declspec(dllexport)
2996 PASCAL FAR TTXBind(WORD Version, TTXExports FAR * exports)
2997 {
2998 #else
2999 BOOL __export PASCAL FAR TTXBind(WORD Version, TTXExports FAR * exports)
3000 {
3001 #endif
3002 int size = sizeof(Exports) - sizeof(exports->size);
3003 /* do version checking if necessary */
3004 /* if (Version!=TTVERSION) return FALSE; */
3005
3006 if (size > exports->size) {
3007 size = exports->size;
3008 }
3009 memcpy((char FAR *) exports + sizeof(exports->size),
3010 (char FAR *) &Exports + sizeof(exports->size), size);
3011 return TRUE;
3012 }
3013
3014 #ifdef TERATERM32
3015 static HANDLE __mem_mapping = NULL;
3016
3017 BOOL WINAPI DllMain(HANDLE hInstance,
3018 ULONG ul_reason_for_call, LPVOID lpReserved)
3019 {
3020 switch (ul_reason_for_call) {
3021 case DLL_THREAD_ATTACH:
3022 /* do thread initialization */
3023 break;
3024 case DLL_THREAD_DETACH:
3025 /* do thread cleanup */
3026 break;
3027 case DLL_PROCESS_ATTACH:
3028 /* do process initialization */
3029 DisableThreadLibraryCalls(hInstance);
3030 hInst = hInstance;
3031 pvar = &InstVar;
3032 __mem_mapping =
3033 CreateFileMapping((HANDLE) 0xFFFFFFFF, NULL, PAGE_READWRITE, 0,
3034 sizeof(TS_SSH), "TTSSH_1-4_TS_data");
3035 if (__mem_mapping == NULL) {
3036 /* fake it. The settings won't be shared, but what the heck. */
3037 pvar->ts_SSH = NULL;
3038 } else {
3039 pvar->ts_SSH =
3040 (TS_SSH *) MapViewOfFile(__mem_mapping, FILE_MAP_WRITE, 0,
3041 0, 0);
3042 }
3043 if (pvar->ts_SSH == NULL) {
3044 /* fake it. The settings won't be shared, but what the heck. */
3045 pvar->ts_SSH = (TS_SSH *) malloc(sizeof(TS_SSH));
3046 if (__mem_mapping != NULL) {
3047 CloseHandle(__mem_mapping);
3048 }
3049 }
3050 break;
3051 case DLL_PROCESS_DETACH:
3052 /* do process cleanup */
3053 if (__mem_mapping == NULL) {
3054 free(pvar->ts_SSH);
3055 } else {
3056 CloseHandle(__mem_mapping);
3057 UnmapViewOfFile(pvar->ts_SSH);
3058 }
3059 break;
3060 }
3061 return TRUE;
3062 }
3063 #else
3064 #ifdef WATCOM
3065 #pragma off (unreferenced);
3066 #endif
3067 int CALLBACK LibMain(HANDLE hInstance, WORD wDataSegment,
3068 WORD wHeapSize, LPSTR lpszCmdLine)
3069 #ifdef WATCOM
3070 #pragma on (unreferenced);
3071 #endif
3072 {
3073 int i;
3074 for (i = 0; i < MAXNUMINST; i++)
3075 TaskList[i] = NULL;
3076 hInst = hInstance;
3077 return (1);
3078 }
3079 #endif
3080
3081
3082 /*
3083 * $Log: not supported by cvs2svn $
3084 * Revision 1.23 2005/06/19 09:17:47 yutakakn
3085 * SSH2 port-fowarding(local to remote)���T�|�[�g�����B
3086 *
3087 * Revision 1.22 2005/05/15 09:14:04 yutakakn
3088 * zlib version�����u�����B
3089 *
3090 * Revision 1.21 2005/05/10 16:44:08 yutakakn
3091 * zlib���o�[�W�������o�[�W�������������������B
3092 *
3093 * Revision 1.20 2005/04/23 17:26:57 yutakakn
3094 * �L�[�����_�C�A���O�������B
3095 *
3096 * Revision 1.19 2005/04/08 14:55:03 yutakakn
3097 * "Duplicate session"��������SSH�������O�C�����s�������������B
3098 *
3099 * Revision 1.18 2005/04/03 14:39:48 yutakakn
3100 * SSH2 channel lookup�@�\�������i�|�[�g�t�H���[�f�B���O�������j�B
3101 * TTSSH 2.10����������log dump�@�\���������ADH������������buffer free��
3102 * �A�v���P�[�V�������������������o�O���C���B
3103 *
3104 * Revision 1.17 2005/03/27 04:39:55 yutakakn
3105 * SSH2�����O����(verbose)���f�[�^�����������B
3106 *
3107 * Revision 1.16 2005/03/23 12:39:20 yutakakn
3108 * �V���A���|�[�g���J������������Alt-N���V�K�������J���������������A�t�H�[�J�X�������������������B
3109 *
3110 * Revision 1.15 2005/03/12 15:07:34 yutakakn
3111 * SSH2 keyboard-interactive�F����TIS�_�C�A���O�����������B
3112 *
3113 * Revision 1.14 2005/03/12 12:08:05 yutakakn
3114 * �p�X���[�h�F�����O���s��keyboard-interactive���\�b�h���A�f�t�H���g�����l������(0)�������B
3115 * �����A�F���_�C�A��