int linux_funkeys;
int app_cursor;
int app_keypad;
+ int nethack_keypad;
+ int alt_f4; /* is it special? */
+ int alt_space; /* is it special? */
/* Terminal options */
int savelines;
int dec_om;
int do_config (void);
int do_reconfig (HWND);
void do_defaults (char *);
-void lognegot (char *);
-void shownegot (HWND);
+void logevent (char *);
+void showeventlog (HWND);
void showabout (HWND);
void verify_ssh_host_key(char *host, struct RSAKey *key);
void get_sesslist(int allocate);
}
static int do_ssh_init(void) {
- char c;
+ char c, *vsp;
char version[10];
- char vstring[40];
+ char vstring[80];
+ char vlog[sizeof(vstring)+20];
int i;
#ifdef FWHACK
if (s_read(&c,1) != 1 || c != 'S') return 0;
if (s_read(&c,1) != 1 || c != 'H') return 0;
#endif
+ strcpy(vstring, "SSH-");
+ vsp = vstring+4;
if (s_read(&c,1) != 1 || c != '-') return 0;
i = 0;
while (1) {
if (s_read(&c,1) != 1)
return 0;
+ if (vsp < vstring+sizeof(vstring)-1)
+ *vsp++ = c;
if (i >= 0) {
if (c == '-') {
version[i] = '\0';
break;
}
+ *vsp = 0;
+ sprintf(vlog, "Server version: %s", vstring);
+ vlog[strcspn(vlog, "\r\n")] = '\0';
+ logevent(vlog);
+
sprintf(vstring, "SSH-%s-PuTTY\n",
(ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"));
+ sprintf(vlog, "We claim version: %s", vstring);
+ vlog[strcspn(vlog, "\r\n")] = '\0';
+ logevent(vlog);
s_write(vstring, strlen(vstring));
return 1;
}
if (pktin.type != SSH_SMSG_PUBLIC_KEY)
fatalbox("Public key packet not received");
- memcpy(cookie, pktin.body, 8);
+ logevent("Received public keys");
- MD5Init(&md5c);
+ memcpy(cookie, pktin.body, 8);
i = makekey(pktin.body+8, &servkey, &keystr1);
j = makekey(pktin.body+8+i, &hostkey, &keystr2);
+ /*
+ * Hash the host key and print the hash in the log box. Just as
+ * a last resort in case the registry's host key checking is
+ * compromised, we'll allow the user some ability to verify
+ * host keys by eye.
+ */
+ MD5Init(&md5c);
+ MD5Update(&md5c, keystr2, hostkey.bytes);
+ MD5Final(session_id, &md5c);
+ {
+ char logmsg[80];
+ int i;
+ logevent("Host key MD5 is:");
+ strcpy(logmsg, " ");
+ for (i = 0; i < 16; i++)
+ sprintf(logmsg+strlen(logmsg), "%02x", session_id[i]);
+ logevent(logmsg);
+ }
+
supported_ciphers_mask = ((pktin.body[12+i+j] << 24) |
(pktin.body[13+i+j] << 16) |
(pktin.body[14+i+j] << 8) |
(pktin.body[18+i+j] << 8) |
(pktin.body[19+i+j]));
+ MD5Init(&md5c);
+
MD5Update(&md5c, keystr2, hostkey.bytes);
MD5Update(&md5c, keystr1, servkey.bytes);
MD5Update(&md5c, pktin.body, 8);
rsaencrypt(rsabuf, hostkey.bytes, &servkey);
}
+ logevent("Encrypted session key");
+
cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH :
cfg.cipher == CIPHER_DES ? SSH_CIPHER_DES :
SSH_CIPHER_3DES;
c_write("Selected cipher not supported, falling back to 3DES\r\n", 53);
cipher_type = SSH_CIPHER_3DES;
}
+ switch (cipher_type) {
+ case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break;
+ case SSH_CIPHER_DES: logevent("Using single-DES encryption"); break;
+ case SSH_CIPHER_BLOWFISH: logevent("Using Blowfish encryption"); break;
+ }
s_wrpkt_start(SSH_CMSG_SESSION_KEY, len+15);
pktout.body[0] = cipher_type;
pktout.body[len+11] = pktout.body[len+12] = 0; /* protocol flags */
pktout.body[len+13] = pktout.body[len+14] = 0;
s_wrpkt();
+ logevent("Trying to enable encryption...");
free(rsabuf);
if (pktin.type != SSH_SMSG_SUCCESS)
fatalbox("Encryption not successfully enabled");
+ logevent("Successfully started encryption");
+
fflush(stdout);
{
static char username[100];
c_write(stuff, strlen(stuff));
}
s_wrpkt_start(SSH_CMSG_USER, 4+strlen(username));
+ {
+ char userlog[20+sizeof(username)];
+ sprintf(userlog, "Sent username \"%s\"", username);
+ logevent(userlog);
+ }
pktout.body[0] = pktout.body[1] = pktout.body[2] = 0;
pktout.body[3] = strlen(username);
memcpy(pktout.body+4, username, strlen(username));
cfg.try_tis_auth &&
(supported_auths_mask & (1<<SSH_AUTH_TIS))) {
pwpkt_type = SSH_CMSG_AUTH_TIS_RESPONSE;
+ logevent("Requested TIS authentication");
s_wrpkt_start(SSH_CMSG_AUTH_TIS, 0);
s_wrpkt();
do { crReturnV; } while (!ispkt);
if (pktin.type != SSH_SMSG_AUTH_TIS_CHALLENGE) {
+ logevent("TIS authentication declined");
c_write("TIS authentication refused.\r\n", 29);
} else {
int challengelen = ((pktin.body[0] << 24) |
(pktin.body[1] << 16) |
(pktin.body[2] << 8) |
(pktin.body[3]));
+ logevent("Received TIS challenge");
c_write(pktin.body+4, challengelen);
}
}
pktout.body[3] = strlen(password);
memcpy(pktout.body+4, password, strlen(password));
s_wrpkt();
+ logevent("Sent password");
memset(password, 0, strlen(password));
do { crReturnV; } while (!ispkt);
if (pktin.type == 15) {
c_write("Access denied\r\n", 15);
+ logevent("Authentication refused");
} else if (pktin.type != 14) {
fatalbox("Strange packet received, type %d", pktin.type);
}
}
+ logevent("Authentication successful");
+
if (!cfg.nopty) {
i = strlen(cfg.termtype);
s_wrpkt_start(SSH_CMSG_REQUEST_PTY, i+5*4+1);
} else if (pktin.type == SSH_SMSG_FAILURE) {
c_write("Server refused to allocate pty\r\n", 32);
}
+ logevent("Allocated pty");
}
s_wrpkt_start(SSH_CMSG_EXEC_SHELL, 0);
s_wrpkt();
+ logevent("Started session");
ssh_state = SSH_STATE_SESSION;
if (size_needed)
c_write(pktin.body+4, len);
} else if (pktin.type == SSH_MSG_DISCONNECT) {
ssh_state = SSH_STATE_CLOSED;
+ logevent("Received disconnect request");
} else if (pktin.type == SSH_SMSG_SUCCESS) {
/* may be from EXEC_SHELL on some servers */
} else if (pktin.type == SSH_SMSG_FAILURE) {
int random_byte(void);
void random_add_noise(void *noise, int length);
+
+void logevent (char *);
ectx.iv0 = 0;
ectx.iv1 = 0;
dctx = ectx;
+ logevent("Initialised Blowfish encryption");
}
static void blowfish_encrypt_blk(unsigned char *blk, int len)
memset(div1, 0, sizeof(div1));
memset(div2, 0, sizeof(div2));
memset(div3, 0, sizeof(div3));
+ logevent("Initialised triple-DES encryption");
}
static void des3_encrypt_blk(unsigned char *blk, int len) {
memset(eiv1, 0, sizeof(eiv1));
des_set_key(key, &dkey1);
memset(div1, 0, sizeof(div1));
+ logevent("Initialised single-DES encryption");
}
static void des_encrypt_blk(unsigned char *blk, int len) {
(cmd == WILL ? "WILL" : cmd == WONT ? "WONT" :
cmd == DO ? "DO" : cmd == DONT ? "DONT" : "<??>"),
telopt(option));
- lognegot(buf);
+ logevent(buf);
}
static void send_opt (int cmd, int option) {
n = 4 + strlen(cfg.termspeed);
b[n] = IAC; b[n+1] = SE;
s_write (b, n+2);
- lognegot("server:\tSB TSPEED SEND");
+ logevent("server:\tSB TSPEED SEND");
sprintf(logbuf, "client:\tSB TSPEED IS %s", cfg.termspeed);
- lognegot (logbuf);
+ logevent (logbuf);
} else
- lognegot ("server:\tSB TSPEED <something weird>");
+ logevent ("server:\tSB TSPEED <something weird>");
break;
case TELOPT_TTYPE:
if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) {
b[n+4] = IAC; b[n+5] = SE;
s_write (b, n+6);
b[n+4] = 0;
- lognegot("server:\tSB TTYPE SEND");
+ logevent("server:\tSB TTYPE SEND");
sprintf(logbuf, "client:\tSB TTYPE IS %s", b+4);
- lognegot(logbuf);
+ logevent(logbuf);
} else
- lognegot("server:\tSB TTYPE <something weird>\r\n");
+ logevent("server:\tSB TTYPE <something weird>\r\n");
break;
case TELOPT_OLD_ENVIRON:
case TELOPT_NEW_ENVIRON:
char logbuf[50];
p++;
sprintf (logbuf, "server:\tSB %s SEND", telopt(sb_opt));
- lognegot (logbuf);
+ logevent (logbuf);
if (sb_opt == TELOPT_OLD_ENVIRON) {
if (cfg.rfc_environ) {
value = RFC_VALUE;
s_write (b, n);
sprintf(logbuf, "client:\tSB %s IS %s", telopt(sb_opt),
n==6 ? "<nothing>" : "<stuff>");
- lognegot (logbuf);
+ logevent (logbuf);
}
break;
}
sprintf(logbuf, "client:\tSB NAWS %d,%d",
((unsigned char)b[3] << 8) + (unsigned char)b[4],
((unsigned char)b[5] << 8) + (unsigned char)b[6]);
- lognegot (logbuf);
+ logevent (logbuf);
}
/*
#define IDC1_KPSTATIC 1010
#define IDC1_KPNORMAL 1011
#define IDC1_KPAPPLIC 1012
-#define IDC1_CURSTATIC 1013
-#define IDC1_CURNORMAL 1014
-#define IDC1_CURAPPLIC 1015
+#define IDC1_KPNH 1013
+#define IDC1_CURSTATIC 1014
+#define IDC1_CURNORMAL 1015
+#define IDC1_CURAPPLIC 1016
+#define IDC1_ALTF4 1017
+#define IDC1_ALTSPACE 1018
#define IDC2_WRAPMODE 1001
#define IDC2_DECOM 1002
FONT 8, "MS Sans Serif"
BEGIN
LTEXT "Action of Backspace:", IDC1_DELSTATIC, 3, 3, 162, 8
- AUTORADIOBUTTON "Control-&H (ASCII 8)", IDC1_DEL008, 3, 12, 162, 10, WS_GROUP
- AUTORADIOBUTTON "Control-&? (ASCII 127)", IDC1_DEL127, 3, 22, 162, 10
- LTEXT "Action of Home and End:", IDC1_HOMESTATIC, 3, 35, 162, 8
- AUTORADIOBUTTON "&Standard (ESC [ 1 ~ and ESC [ 4 ~)",
- IDC1_HOMETILDE, 3, 44, 162, 10, WS_GROUP
- AUTORADIOBUTTON "&rxvt (ESC [ H and ESC O w)",
- IDC1_HOMERXVT, 3, 54, 162, 10
- LTEXT "Action of F1 through F5:", IDC1_FUNCSTATIC, 3, 67, 162, 8
- AUTORADIOBUTTON "S&tandard (ESC [ 11 ~ through ESC [ 15 ~)",
- IDC1_FUNCTILDE, 3, 76, 162, 10, WS_GROUP
- AUTORADIOBUTTON "&Linux (ESC [ [ A through ESC [ [ E)",
- IDC1_FUNCLINUX, 3, 86, 162, 10
- LTEXT "Initial state of numeric keypad:", IDC1_KPSTATIC, 3, 99, 162, 8
- AUTORADIOBUTTON "&Normal (depends on NumLock)",
- IDC1_KPNORMAL, 3, 108, 162, 10, WS_GROUP
- AUTORADIOBUTTON "A&pplication (ESC O P etc)",
- IDC1_KPAPPLIC, 3, 118, 162, 10
- LTEXT "Initial state of cursor keys:", IDC1_CURSTATIC, 3, 131, 162, 8
- AUTORADIOBUTTON "Nor&mal (ESC [ A through ESC [ D)",
- IDC1_CURNORMAL, 3, 140, 162, 10, WS_GROUP
- AUTORADIOBUTTON "Appl&ication (ESC O A through ESC O D)",
- IDC1_CURAPPLIC, 3, 150, 162, 10
+ AUTORADIOBUTTON "Control-&H", IDC1_DEL008, 3, 12, 50, 10, WS_GROUP
+ AUTORADIOBUTTON "Control-&? (127)", IDC1_DEL127, 62, 12, 100, 10
+ LTEXT "Action of Home and End:", IDC1_HOMESTATIC, 3, 25, 162, 8
+ AUTORADIOBUTTON "&Standard",
+ IDC1_HOMETILDE, 3, 34, 50, 10, WS_GROUP
+ AUTORADIOBUTTON "&rxvt",
+ IDC1_HOMERXVT, 62, 34, 50, 10
+ LTEXT "Action of F1 through F5:", IDC1_FUNCSTATIC, 3, 47, 162, 8
+ AUTORADIOBUTTON "S&tandard",
+ IDC1_FUNCTILDE, 3, 56, 50, 10, WS_GROUP
+ AUTORADIOBUTTON "&Linux console",
+ IDC1_FUNCLINUX, 62, 56, 100, 10
+ LTEXT "Initial state of cursor keys:", IDC1_CURSTATIC, 3, 69, 162, 8
+ AUTORADIOBUTTON "&Normal",
+ IDC1_CURNORMAL, 3, 78, 50, 10, WS_GROUP
+ AUTORADIOBUTTON "A&pplication",
+ IDC1_CURAPPLIC, 62, 78, 50, 10
+ LTEXT "Initial state of numeric keypad:", IDC1_KPSTATIC, 3, 91, 162, 8
+ AUTORADIOBUTTON "Nor&mal",
+ IDC1_KPNORMAL, 3, 100, 50, 10, WS_GROUP
+ AUTORADIOBUTTON "Appl&ication",
+ IDC1_KPAPPLIC, 62, 100, 50, 10
+ AUTORADIOBUTTON "N&etHack",
+ IDC1_KPNH, 121, 100, 50, 10
+ AUTOCHECKBOX "ALT-F&4 is special (closes window)",
+ IDC1_ALTF4, 3, 113, 162, 10
+ AUTOCHECKBOX "ALT-Space is special (S&ystem menu)",
+ IDC1_ALTSPACE, 3, 123, 162, 10
END
IDD_PANEL2 DIALOG DISCARDABLE 6, 30, 168, 163
IDD_LOGBOX DIALOG DISCARDABLE 100, 20, 160, 119
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
-CAPTION "PuTTY Negotiation Log"
+CAPTION "PuTTY Event Log"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "&Close", IDOK, 58, 102, 44, 14
static const char *const puttystr = PUTTY_REG_POS "\\Sessions";
-static char **negots = NULL;
-static int nnegots = 0, negsize = 0;
+static char **events = NULL;
+static int nevents = 0, negsize = 0;
+
static HWND logbox = NULL, abtbox = NULL;
static char hex[16] = "0123456789ABCDEF";
wppi (sesskey, "LinuxFunctionKeys", cfg.linux_funkeys);
wppi (sesskey, "ApplicationCursorKeys", cfg.app_cursor);
wppi (sesskey, "ApplicationKeypad", cfg.app_keypad);
+ wppi (sesskey, "NetHackKeypad", cfg.nethack_keypad);
+ wppi (sesskey, "AltF4", cfg.alt_f4);
+ wppi (sesskey, "AltSpace", cfg.alt_space);
wppi (sesskey, "ScrollbackLines", cfg.savelines);
wppi (sesskey, "DECOriginMode", cfg.dec_om);
wppi (sesskey, "AutoWrapMode", cfg.wrap_mode);
gppi (sesskey, "LinuxFunctionKeys", 0, &cfg.linux_funkeys);
gppi (sesskey, "ApplicationCursorKeys", 0, &cfg.app_cursor);
gppi (sesskey, "ApplicationKeypad", 0, &cfg.app_keypad);
+ gppi (sesskey, "NetHackKeypad", 0, &cfg.nethack_keypad);
+ gppi (sesskey, "AltF4", 1, &cfg.alt_f4);
+ gppi (sesskey, "AltSpace", 0, &cfg.alt_space);
gppi (sesskey, "ScrollbackLines", 200, &cfg.savelines);
gppi (sesskey, "DECOriginMode", 0, &cfg.dec_om);
gppi (sesskey, "AutoWrapMode", 1, &cfg.wrap_mode);
switch (msg) {
case WM_INITDIALOG:
- for (i=0; i<nnegots; i++)
+ for (i=0; i<nevents; i++)
SendDlgItemMessage (hwnd, IDN_LIST, LB_ADDSTRING,
- 0, (LPARAM)negots[i]);
+ 0, (LPARAM)events[i]);
return 1;
/* case WM_CTLCOLORDLG: */
/* return (int) GetStockObject (LTGRAY_BRUSH); */
cfg.linux_funkeys ? IDC1_FUNCLINUX : IDC1_FUNCTILDE);
CheckRadioButton (hwnd, IDC1_CURNORMAL, IDC1_CURAPPLIC,
cfg.app_cursor ? IDC1_CURAPPLIC : IDC1_CURNORMAL);
- CheckRadioButton (hwnd, IDC1_KPNORMAL, IDC1_KPAPPLIC,
+ CheckRadioButton (hwnd, IDC1_KPNORMAL, IDC1_KPNH,
+ cfg.nethack_keypad ? IDC1_KPNH :
cfg.app_keypad ? IDC1_KPAPPLIC : IDC1_KPNORMAL);
+ CheckDlgButton (hwnd, IDC1_ALTF4, cfg.alt_f4);
+ CheckDlgButton (hwnd, IDC1_ALTSPACE, cfg.alt_space);
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED ||
case IDC1_KPNORMAL:
case IDC1_KPAPPLIC:
cfg.app_keypad = IsDlgButtonChecked (hwnd, IDC1_KPAPPLIC);
+ cfg.nethack_keypad = FALSE;
+ break;
+ case IDC1_KPNH:
+ cfg.app_keypad = FALSE;
+ cfg.nethack_keypad = TRUE;
break;
case IDC1_CURNORMAL:
case IDC1_CURAPPLIC:
cfg.app_cursor = IsDlgButtonChecked (hwnd, IDC1_CURAPPLIC);
break;
+ case IDC1_ALTF4:
+ if (HIWORD(wParam) == BN_CLICKED ||
+ HIWORD(wParam) == BN_DOUBLECLICKED)
+ cfg.alt_f4 = IsDlgButtonChecked (hwnd, IDC1_ALTF4);
+ break;
+ case IDC1_ALTSPACE:
+ if (HIWORD(wParam) == BN_CLICKED ||
+ HIWORD(wParam) == BN_DOUBLECLICKED)
+ cfg.alt_space = IsDlgButtonChecked (hwnd, IDC1_ALTSPACE);
+ break;
}
}
return GeneralPanelProc (hwnd, msg, wParam, lParam);
load_settings ("Default Settings", FALSE);
}
-void lognegot (char *string) {
- if (nnegots >= negsize) {
+void logevent (char *string) {
+ if (nevents >= negsize) {
negsize += 64;
- negots = srealloc (negots, negsize * sizeof(*negots));
+ events = srealloc (events, negsize * sizeof(*events));
}
- negots[nnegots] = smalloc(1+strlen(string));
- strcpy (negots[nnegots], string);
- nnegots++;
+ events[nevents] = smalloc(1+strlen(string));
+ strcpy (events[nevents], string);
+ nevents++;
if (logbox)
SendDlgItemMessage (logbox, IDN_LIST, LB_ADDSTRING,
0, (LPARAM)string);
}
-void shownegot (HWND hwnd) {
+void showeventlog (HWND hwnd) {
if (!logbox) {
logbox = CreateDialog (hinst, MAKEINTRESOURCE(IDD_LOGBOX),
hwnd, LogProc);
AppendMenu (p, MF_ENABLED, IDM_TEL_EOR, "End Of Record");
AppendMenu (p, MF_ENABLED, IDM_TEL_EOF, "End Of File");
AppendMenu (m, MF_POPUP | MF_ENABLED, (UINT) p, "Telnet Command");
- AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Show Negotiation");
AppendMenu (m, MF_SEPARATOR, 0, 0);
}
+ AppendMenu (m, MF_ENABLED, IDM_SHOWLOG, "Event Log");
+ AppendMenu (m, MF_SEPARATOR, 0, 0);
AppendMenu (m, MF_ENABLED, IDM_NEWSESS, "New Session");
AppendMenu (m, MF_ENABLED, IDM_DUPSESS, "Duplicate Session");
s = CreateMenu();
case WM_SYSCOMMAND:
switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */
case IDM_SHOWLOG:
- shownegot(hwnd);
+ showeventlog(hwnd);
break;
case IDM_NEWSESS:
case IDM_DUPSESS:
int len;
len = TranslateKey (wParam, lParam, buf);
+ if (len == -1)
+ return DefWindowProc (hwnd, message, wParam, lParam);
back->send (buf, len);
}
return 0;
SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
return 0;
}
- if ((lParam & 0x20000000) && wParam == VK_F4) {
- SendMessage (hwnd, WM_CLOSE, 0, 0);
- return 0;
+ if ((lParam & 0x20000000) && wParam == VK_F4 && cfg.alt_f4) {
+ return -1;
+ }
+ if ((lParam & 0x20000000) && wParam == VK_SPACE && cfg.alt_space) {
+ SendMessage (hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
+ return -1;
}
/*
return p - output;
}
- /*
- * If we're in applications keypad mode, we have to process it
- * before char-map translation, because it will pre-empt lots
- * of stuff, even if NumLock is off.
- */
- if (app_keypad_keys) {
+ if (cfg.nethack_keypad) {
+ int shift = keystate[VK_SHIFT] & 0x80;
+ /*
+ * NB the shifted versions only work with numlock off.
+ */
+ switch ( (lParam >> 16) & 0x1FF ) {
+ case 0x047: *p++ = shift ? 'Y' : 'y'; return p - output;
+ case 0x048: *p++ = shift ? 'K' : 'k'; return p - output;
+ case 0x049: *p++ = shift ? 'U' : 'u'; return p - output;
+ case 0x04B: *p++ = shift ? 'H' : 'h'; return p - output;
+ case 0x04C: *p++ = '.'; return p - output;
+ case 0x04D: *p++ = shift ? 'L' : 'l'; return p - output;
+ case 0x04F: *p++ = shift ? 'B' : 'b'; return p - output;
+ case 0x050: *p++ = shift ? 'J' : 'j'; return p - output;
+ case 0x051: *p++ = shift ? 'N' : 'n'; return p - output;
+ case 0x053: *p++ = '.'; return p - output;
+ }
+ } else if (app_keypad_keys) {
+ /*
+ * If we're in applications keypad mode, we have to process it
+ * before char-map translation, because it will pre-empt lots
+ * of stuff, even if NumLock is off.
+ */
if (ret) {
/*
* Hack to ensure NumLock doesn't interfere with