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 2901 - (show annotations) (download) (as text)
Mon Aug 21 12:21:33 2006 UTC (17 years, 7 months ago) by maya
Original Path: ttssh2/trunk/ttxssh/ttxssh.c
File MIME type: text/x-csrc
File size: 88990 byte(s)
コマンドラインパラメータにおいて、ダブルクォーテーションで囲まれたファイル名を正しく認識するようにした。

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