#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
+#ifndef AUTO_WINSOCK
+#ifdef WINSOCK_TWO
+#include <winsock2.h>
+#else
#include <winsock.h>
+#endif
+#endif
#include "putty.h"
#include "tree234.h"
#endif
#define logevent(s) { logevent(s); \
- if (!(flags & FLAG_CONNECTION) && (flags & FLAG_VERBOSE)) \
+ if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
fprintf(stderr, "%s\n", s); }
-#define SSH1_MSG_DISCONNECT 1
-#define SSH1_SMSG_PUBLIC_KEY 2
-#define SSH1_CMSG_SESSION_KEY 3
-#define SSH1_CMSG_USER 4
-#define SSH1_CMSG_AUTH_RSA 6
-#define SSH1_SMSG_AUTH_RSA_CHALLENGE 7
-#define SSH1_CMSG_AUTH_RSA_RESPONSE 8
-#define SSH1_CMSG_AUTH_PASSWORD 9
-#define SSH1_CMSG_REQUEST_PTY 10
-#define SSH1_CMSG_WINDOW_SIZE 11
-#define SSH1_CMSG_EXEC_SHELL 12
-#define SSH1_CMSG_EXEC_CMD 13
-#define SSH1_SMSG_SUCCESS 14
-#define SSH1_SMSG_FAILURE 15
-#define SSH1_CMSG_STDIN_DATA 16
-#define SSH1_SMSG_STDOUT_DATA 17
-#define SSH1_SMSG_STDERR_DATA 18
-#define SSH1_CMSG_EOF 19
-#define SSH1_SMSG_EXIT_STATUS 20
-#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION 21
-#define SSH1_MSG_CHANNEL_OPEN_FAILURE 22
-#define SSH1_MSG_CHANNEL_DATA 23
-#define SSH1_MSG_CHANNEL_CLOSE 24
-#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25
-#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30
-#define SSH1_SMSG_AGENT_OPEN 31
-#define SSH1_CMSG_EXIT_CONFIRMATION 33
-#define SSH1_MSG_IGNORE 32
-#define SSH1_MSG_DEBUG 36
-#define SSH1_CMSG_AUTH_TIS 39
-#define SSH1_SMSG_AUTH_TIS_CHALLENGE 40
-#define SSH1_CMSG_AUTH_TIS_RESPONSE 41
-#define SSH1_CMSG_AUTH_CCARD 70
-#define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71
-#define SSH1_CMSG_AUTH_CCARD_RESPONSE 72
-
-#define SSH1_AUTH_TIS 5
-#define SSH1_AUTH_CCARD 16
-
-#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1
-#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2
-#define SSH_AGENTC_RSA_CHALLENGE 3
-#define SSH_AGENT_RSA_RESPONSE 4
-#define SSH_AGENT_FAILURE 5
-#define SSH_AGENT_SUCCESS 6
-#define SSH_AGENTC_ADD_RSA_IDENTITY 7
-#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8
-
-#define SSH2_MSG_DISCONNECT 1
-#define SSH2_MSG_IGNORE 2
-#define SSH2_MSG_UNIMPLEMENTED 3
-#define SSH2_MSG_DEBUG 4
-#define SSH2_MSG_SERVICE_REQUEST 5
-#define SSH2_MSG_SERVICE_ACCEPT 6
-#define SSH2_MSG_KEXINIT 20
-#define SSH2_MSG_NEWKEYS 21
-#define SSH2_MSG_KEXDH_INIT 30
-#define SSH2_MSG_KEXDH_REPLY 31
-#define SSH2_MSG_USERAUTH_REQUEST 50
-#define SSH2_MSG_USERAUTH_FAILURE 51
-#define SSH2_MSG_USERAUTH_SUCCESS 52
-#define SSH2_MSG_USERAUTH_BANNER 53
-#define SSH2_MSG_USERAUTH_PK_OK 60
-#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60
-#define SSH2_MSG_GLOBAL_REQUEST 80
-#define SSH2_MSG_REQUEST_SUCCESS 81
-#define SSH2_MSG_REQUEST_FAILURE 82
-#define SSH2_MSG_CHANNEL_OPEN 90
-#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91
-#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92
-#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93
-#define SSH2_MSG_CHANNEL_DATA 94
-#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95
-#define SSH2_MSG_CHANNEL_EOF 96
-#define SSH2_MSG_CHANNEL_CLOSE 97
-#define SSH2_MSG_CHANNEL_REQUEST 98
-#define SSH2_MSG_CHANNEL_SUCCESS 99
-#define SSH2_MSG_CHANNEL_FAILURE 100
-
-#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1
-#define SSH2_OPEN_CONNECT_FAILED 2
-#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3
-#define SSH2_OPEN_RESOURCE_SHORTAGE 4
-#define SSH2_EXTENDED_DATA_STDERR 1
+#define bombout(msg) ( ssh_state == SSH_STATE_CLOSED, closesocket(s), \
+ s = INVALID_SOCKET, connection_fatal msg )
+
+#define SSH1_MSG_DISCONNECT 1 /* 0x1 */
+#define SSH1_SMSG_PUBLIC_KEY 2 /* 0x2 */
+#define SSH1_CMSG_SESSION_KEY 3 /* 0x3 */
+#define SSH1_CMSG_USER 4 /* 0x4 */
+#define SSH1_CMSG_AUTH_RSA 6 /* 0x6 */
+#define SSH1_SMSG_AUTH_RSA_CHALLENGE 7 /* 0x7 */
+#define SSH1_CMSG_AUTH_RSA_RESPONSE 8 /* 0x8 */
+#define SSH1_CMSG_AUTH_PASSWORD 9 /* 0x9 */
+#define SSH1_CMSG_REQUEST_PTY 10 /* 0xa */
+#define SSH1_CMSG_WINDOW_SIZE 11 /* 0xb */
+#define SSH1_CMSG_EXEC_SHELL 12 /* 0xc */
+#define SSH1_CMSG_EXEC_CMD 13 /* 0xd */
+#define SSH1_SMSG_SUCCESS 14 /* 0xe */
+#define SSH1_SMSG_FAILURE 15 /* 0xf */
+#define SSH1_CMSG_STDIN_DATA 16 /* 0x10 */
+#define SSH1_SMSG_STDOUT_DATA 17 /* 0x11 */
+#define SSH1_SMSG_STDERR_DATA 18 /* 0x12 */
+#define SSH1_CMSG_EOF 19 /* 0x13 */
+#define SSH1_SMSG_EXIT_STATUS 20 /* 0x14 */
+#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION 21 /* 0x15 */
+#define SSH1_MSG_CHANNEL_OPEN_FAILURE 22 /* 0x16 */
+#define SSH1_MSG_CHANNEL_DATA 23 /* 0x17 */
+#define SSH1_MSG_CHANNEL_CLOSE 24 /* 0x18 */
+#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION 25 /* 0x19 */
+#define SSH1_CMSG_AGENT_REQUEST_FORWARDING 30 /* 0x1e */
+#define SSH1_SMSG_AGENT_OPEN 31 /* 0x1f */
+#define SSH1_CMSG_EXIT_CONFIRMATION 33 /* 0x21 */
+#define SSH1_MSG_IGNORE 32 /* 0x20 */
+#define SSH1_MSG_DEBUG 36 /* 0x24 */
+#define SSH1_CMSG_AUTH_TIS 39 /* 0x27 */
+#define SSH1_SMSG_AUTH_TIS_CHALLENGE 40 /* 0x28 */
+#define SSH1_CMSG_AUTH_TIS_RESPONSE 41 /* 0x29 */
+#define SSH1_CMSG_AUTH_CCARD 70 /* 0x46 */
+#define SSH1_SMSG_AUTH_CCARD_CHALLENGE 71 /* 0x47 */
+#define SSH1_CMSG_AUTH_CCARD_RESPONSE 72 /* 0x48 */
+
+#define SSH1_AUTH_TIS 5 /* 0x5 */
+#define SSH1_AUTH_CCARD 16 /* 0x10 */
+
+#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 /* 0x1 */
+#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 /* 0x2 */
+#define SSH_AGENTC_RSA_CHALLENGE 3 /* 0x3 */
+#define SSH_AGENT_RSA_RESPONSE 4 /* 0x4 */
+#define SSH_AGENT_FAILURE 5 /* 0x5 */
+#define SSH_AGENT_SUCCESS 6 /* 0x6 */
+#define SSH_AGENTC_ADD_RSA_IDENTITY 7 /* 0x7 */
+#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 /* 0x8 */
+
+#define SSH2_MSG_DISCONNECT 1 /* 0x1 */
+#define SSH2_MSG_IGNORE 2 /* 0x2 */
+#define SSH2_MSG_UNIMPLEMENTED 3 /* 0x3 */
+#define SSH2_MSG_DEBUG 4 /* 0x4 */
+#define SSH2_MSG_SERVICE_REQUEST 5 /* 0x5 */
+#define SSH2_MSG_SERVICE_ACCEPT 6 /* 0x6 */
+#define SSH2_MSG_KEXINIT 20 /* 0x14 */
+#define SSH2_MSG_NEWKEYS 21 /* 0x15 */
+#define SSH2_MSG_KEXDH_INIT 30 /* 0x1e */
+#define SSH2_MSG_KEXDH_REPLY 31 /* 0x1f */
+#define SSH2_MSG_USERAUTH_REQUEST 50 /* 0x32 */
+#define SSH2_MSG_USERAUTH_FAILURE 51 /* 0x33 */
+#define SSH2_MSG_USERAUTH_SUCCESS 52 /* 0x34 */
+#define SSH2_MSG_USERAUTH_BANNER 53 /* 0x35 */
+#define SSH2_MSG_USERAUTH_PK_OK 60 /* 0x3c */
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ 60 /* 0x3c */
+#define SSH2_MSG_GLOBAL_REQUEST 80 /* 0x50 */
+#define SSH2_MSG_REQUEST_SUCCESS 81 /* 0x51 */
+#define SSH2_MSG_REQUEST_FAILURE 82 /* 0x52 */
+#define SSH2_MSG_CHANNEL_OPEN 90 /* 0x5a */
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION 91 /* 0x5b */
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE 92 /* 0x5c */
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST 93 /* 0x5d */
+#define SSH2_MSG_CHANNEL_DATA 94 /* 0x5e */
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA 95 /* 0x5f */
+#define SSH2_MSG_CHANNEL_EOF 96 /* 0x60 */
+#define SSH2_MSG_CHANNEL_CLOSE 97 /* 0x61 */
+#define SSH2_MSG_CHANNEL_REQUEST 98 /* 0x62 */
+#define SSH2_MSG_CHANNEL_SUCCESS 99 /* 0x63 */
+#define SSH2_MSG_CHANNEL_FAILURE 100 /* 0x64 */
+
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED 1 /* 0x1 */
+#define SSH2_OPEN_CONNECT_FAILED 2 /* 0x2 */
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE 3 /* 0x3 */
+#define SSH2_OPEN_RESOURCE_SHORTAGE 4 /* 0x4 */
+
+#define SSH2_EXTENDED_DATA_STDERR 1 /* 0x1 */
#define GET_32BIT(cp) \
(((unsigned long)(unsigned char)(cp)[0] << 24) | \
extern struct ssh_cipher ssh_3des;
extern struct ssh_cipher ssh_3des_ssh2;
extern struct ssh_cipher ssh_des;
-extern struct ssh_cipher ssh_blowfish;
+extern struct ssh_cipher ssh_blowfish_ssh1;
+extern struct ssh_cipher ssh_blowfish_ssh2;
-/* for ssh 2; we miss out single-DES because it isn't supported */
-struct ssh_cipher *ciphers[] = { &ssh_3des_ssh2, &ssh_blowfish };
+/*
+ * Ciphers for SSH2. We miss out single-DES because it isn't
+ * supported; also 3DES and Blowfish are both done differently from
+ * SSH1. (3DES uses outer chaining; Blowfish has the opposite
+ * endianness and different-sized keys.)
+ *
+ * The first entry in this array is set up to be whatever the user
+ * asks for as a cipher. Thereafter there is a fixed preference
+ * order of fallback ciphers.
+ */
+struct ssh_cipher *ciphers[] = { NULL, &ssh_blowfish_ssh2, &ssh_3des_ssh2 };
extern struct ssh_kex ssh_diffiehellman;
struct ssh_kex *kex_algs[] = { &ssh_diffiehellman };
* 2-3-4 tree storing channels.
*/
struct ssh_channel {
- int remoteid, localid;
+ unsigned remoteid, localid;
int type;
int closes;
union {
unsigned char msglen[4];
int lensofar, totallen;
} a;
+ struct ssh2_data_channel {
+ unsigned char *outbuffer;
+ unsigned outbuflen, outbufsize;
+ unsigned remwindow, remmaxpkt;
+ } v2;
} u;
};
static tree234 *ssh_channels; /* indexed by local id */
return 0;
}
static int ssh_channelfind(void *av, void *bv) {
- int *a = (int *)av;
+ unsigned *a = (unsigned *)av;
struct ssh_channel *b = (struct ssh_channel *)bv;
if (*a < b->localid) return -1;
if (*a > b->localid) return +1;
return 0;
}
+static struct ssh_channel *mainchan; /* primary session channel */
+
static enum {
SSH_STATE_BEFORE_SIZE,
SSH_STATE_INTERMED,
while (len > 0) {
int i = send (s, buf, len, 0);
noise_ultralight(i);
- if (i <= 0)
- fatalbox("Lost connection while sending");
+ if (i <= 0) {
+ bombout(("Lost connection while sending"));
+ return;
+ }
if (i > 0)
len -= i, buf += i;
}
}
static void c_write (char *buf, int len) {
- if (!(flags & FLAG_CONNECTION)) {
+ if ((flags & FLAG_STDERR)) {
int i;
for (i = 0; i < len; i++)
if (buf[i] != '\r')
realcrc = crc32(pktin.data, biglen-4);
gotcrc = GET_32BIT(pktin.data+biglen-4);
if (gotcrc != realcrc) {
- fatalbox("Incorrect CRC received on packet");
+ bombout(("Incorrect CRC received on packet"));
+ crReturn(0);
}
if (pktin.type == SSH1_SMSG_STDOUT_DATA ||
pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE ||
pktin.type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
long strlen = GET_32BIT(pktin.body);
- if (strlen + 4 != pktin.length)
- fatalbox("Received data packet with bogus string length");
+ if (strlen + 4 != pktin.length) {
+ bombout(("Received data packet with bogus string length"));
+ crReturn(0);
+ }
}
if (pktin.type == SSH1_MSG_DEBUG) {
/*
* Adjust memory allocation if packet is too big.
*/
- if (pktin.maxlen < packetlen) {
- pktin.maxlen = packetlen;
- pktin.data = (pktin.data == NULL ? malloc(packetlen+APIEXTRA) :
- realloc(pktin.data, packetlen+APIEXTRA));
+ if (pktin.maxlen < packetlen+maclen) {
+ pktin.maxlen = packetlen+maclen;
+ pktin.data = (pktin.data == NULL ? malloc(pktin.maxlen+APIEXTRA) :
+ realloc(pktin.data, pktin.maxlen+APIEXTRA));
if (!pktin.data)
fatalbox("Out of memory");
}
/*
* Check the MAC.
*/
- if (scmac && !scmac->verify(pktin.data, len+4, incoming_sequence))
- fatalbox("Incorrect MAC received on packet");
+ if (scmac && !scmac->verify(pktin.data, len+4, incoming_sequence)) {
+ bombout(("Incorrect MAC received on packet"));
+ crReturn(0);
+ }
incoming_sequence++; /* whether or not we MACed */
pktin.savedpos = 6;
ssh2_pkt_getstring(&p, &length);
if (!p)
return NULL;
- if (p[0] & 0x80)
- fatalbox("internal error: Can't handle negative mpints");
+ if (p[0] & 0x80) {
+ bombout(("internal error: Can't handle negative mpints"));
+ return NULL;
+ }
b = newbn((length+1)/2);
for (i = 0; i < length; i++) {
j = length - 1 - i;
if (!ispkt) crWaitUntil(ispkt);
- if (pktin.type != SSH1_SMSG_PUBLIC_KEY)
- fatalbox("Public key packet not received");
+ if (pktin.type != SSH1_SMSG_PUBLIC_KEY) {
+ bombout(("Public key packet not received"));
+ crReturn(0);
+ }
logevent("Received public keys");
free(rsabuf);
- cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish :
+ cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
cipher_type == SSH_CIPHER_DES ? &ssh_des :
&ssh_3des;
cipher->sesskey(session_key);
crWaitUntil(ispkt);
- if (pktin.type != SSH1_SMSG_SUCCESS)
- fatalbox("Encryption not successfully enabled");
+ if (pktin.type != SSH1_SMSG_SUCCESS) {
+ bombout(("Encryption not successfully enabled"));
+ crReturn(0);
+ }
logevent("Successfully started encryption");
static char username[100];
static int pos = 0;
static char c;
- if ((flags & FLAG_CONNECTION) && !*cfg.username) {
+ if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
c_write("login as: ", 10);
+ ssh_send_ok = 1;
while (pos >= 0) {
crWaitUntil(!ispkt);
while (inlen--) switch (c = *in++) {
char stuff[200];
strncpy(username, cfg.username, 99);
username[99] = '\0';
- if (flags & FLAG_VERBOSE) {
+ if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
sprintf(stuff, "Sent username \"%s\".\r\n", username);
c_write(stuff, strlen(stuff));
}
while (pktin.type == SSH1_SMSG_FAILURE) {
static char password[100];
+ static char prompt[200];
static int pos;
static char c;
static int pwpkt_type;
if (*cfg.keyfile && !tried_publickey)
pwpkt_type = SSH1_CMSG_AUTH_RSA;
- if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD && !FLAG_WINDOWED) {
- char prompt[200];
- sprintf(prompt, "%s@%s's password: ", cfg.username, savedhost);
+ if (pktin.type == SSH1_SMSG_FAILURE &&
+ cfg.try_tis_auth &&
+ (supported_auths_mask & (1<<SSH1_AUTH_TIS))) {
+ pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
+ logevent("Requested TIS authentication");
+ send_packet(SSH1_CMSG_AUTH_TIS, PKT_END);
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
+ logevent("TIS authentication declined");
+ if (flags & FLAG_INTERACTIVE)
+ 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");
+ if (challengelen > sizeof(prompt)-1)
+ challengelen = sizeof(prompt)-1; /* prevent overrun */
+ memcpy(prompt, pktin.body+4, challengelen);
+ prompt[challengelen] = '\0';
+ }
+ }
+ if (pktin.type == SSH1_SMSG_FAILURE &&
+ cfg.try_tis_auth &&
+ (supported_auths_mask & (1<<SSH1_AUTH_CCARD))) {
+ pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
+ logevent("Requested CryptoCard authentication");
+ send_packet(SSH1_CMSG_AUTH_CCARD, PKT_END);
+ crWaitUntil(ispkt);
+ if (pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
+ logevent("CryptoCard authentication declined");
+ c_write("CryptoCard 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 CryptoCard challenge");
+ if (challengelen > sizeof(prompt)-1)
+ challengelen = sizeof(prompt)-1; /* prevent overrun */
+ memcpy(prompt, pktin.body+4, challengelen);
+ strncpy(prompt + challengelen, "\r\nResponse : ",
+ sizeof(prompt)-challengelen);
+ prompt[sizeof(prompt)-1] = '\0';
+ }
+ }
+ if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+ sprintf(prompt, "%.90s@%.90s's password: ",
+ cfg.username, savedhost);
+ }
+ if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
+ char *comment = NULL;
+ if (flags & FLAG_VERBOSE)
+ c_write("Trying public key authentication.\r\n", 35);
+ if (!rsakey_encrypted(cfg.keyfile, &comment)) {
+ if (flags & FLAG_VERBOSE)
+ c_write("No passphrase required.\r\n", 25);
+ goto tryauth;
+ }
+ sprintf(prompt, "Passphrase for key \"%.100s\": ", comment);
+ free(comment);
+ }
+
+ if (!(flags & FLAG_INTERACTIVE)) {
if (!ssh_get_password(prompt, password, sizeof(password))) {
/*
* get_password failed to get a password (for
crReturn(1);
}
} else {
-
- if (pktin.type == SSH1_SMSG_FAILURE &&
- cfg.try_tis_auth &&
- (supported_auths_mask & (1<<SSH1_AUTH_TIS))) {
- pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
- logevent("Requested TIS authentication");
- send_packet(SSH1_CMSG_AUTH_TIS, PKT_END);
- crWaitUntil(ispkt);
- if (pktin.type != SSH1_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);
- }
- }
- if (pktin.type == SSH1_SMSG_FAILURE &&
- cfg.try_tis_auth &&
- (supported_auths_mask & (1<<SSH1_AUTH_CCARD))) {
- pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
- logevent("Requested CryptoCard authentication");
- send_packet(SSH1_CMSG_AUTH_CCARD, PKT_END);
- crWaitUntil(ispkt);
- if (pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
- logevent("CryptoCard authentication declined");
- c_write("CryptoCard 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 CryptoCard challenge");
- c_write(pktin.body+4, challengelen);
- c_write("\r\nResponse : ", 13);
- }
- }
- if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD)
- c_write("password: ", 10);
- if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
- if (flags & FLAG_VERBOSE)
- c_write("Trying public key authentication.\r\n", 35);
- if (!rsakey_encrypted(cfg.keyfile)) {
- if (flags & FLAG_VERBOSE)
- c_write("No passphrase required.\r\n", 25);
- goto tryauth;
- }
- c_write("passphrase: ", 12);
- }
-
+ c_write(prompt, strlen(prompt));
pos = 0;
+ ssh_send_ok = 1;
while (pos >= 0) {
crWaitUntil(!ispkt);
while (inlen--) switch (c = *in++) {
}
}
c_write("\r\n", 2);
-
- }
+ }
tryauth:
if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
crWaitUntil(ispkt);
if (pktin.type == SSH1_SMSG_FAILURE) {
- if (flags & FLAG_VERBOSE)
- c_write("Server refused our public key.\r\n", 32);
+ c_write("Server refused our public key.\r\n", 32);
continue; /* go and try password */
}
- if (pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE)
- fatalbox("Bizarre response to offer of public key");
+ if (pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
+ bombout(("Bizarre response to offer of public key"));
+ crReturn(0);
+ }
ssh1_read_bignum(pktin.body, &challenge);
response = rsadecrypt(challenge, &pubkey);
freebn(pubkey.private_exponent); /* burn the evidence */
45);
continue; /* go and try password */
} else if (pktin.type != SSH1_SMSG_SUCCESS) {
- fatalbox("Bizarre response to RSA authentication response");
+ bombout(("Bizarre response to RSA authentication response"));
+ crReturn(0);
}
break; /* we're through! */
ssh_state = SSH_STATE_CLOSED;
crReturn(1);
} else if (pktin.type != SSH1_SMSG_SUCCESS) {
- fatalbox("Strange packet received, type %d", pktin.type);
+ bombout(("Strange packet received, type %d", pktin.type));
+ crReturn(0);
}
}
send_packet(SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
do { crReturnV; } while (!ispkt);
if (pktin.type != SSH1_SMSG_SUCCESS && pktin.type != SSH1_SMSG_FAILURE) {
- fatalbox("Protocol confusion");
+ bombout(("Protocol confusion"));
+ crReturnV;
} else if (pktin.type == SSH1_SMSG_FAILURE) {
logevent("Agent forwarding refused");
} else
ssh_state = SSH_STATE_INTERMED;
do { crReturnV; } while (!ispkt);
if (pktin.type != SSH1_SMSG_SUCCESS && pktin.type != SSH1_SMSG_FAILURE) {
- fatalbox("Protocol confusion");
+ bombout(("Protocol confusion"));
+ crReturnV;
} else if (pktin.type == SSH1_SMSG_FAILURE) {
c_write("Server refused to allocate pty\r\n", 32);
}
ssh_send_ok = 1;
ssh_channels = newtree234(ssh_channelcmp);
+ begin_session();
while (1) {
crReturnV;
if (ispkt) {
} else if (pktin.type == SSH1_SMSG_AGENT_OPEN) {
/* Remote side is trying to open a channel to talk to our
* agent. Give them back a local channel number. */
- int i = 1;
+ unsigned i = 1;
struct ssh_channel *c;
enum234 e;
for (c = first234(ssh_channels, &e); c; c = next234(&e)) {
} else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
/* Remote side closes a channel. */
- int i = GET_32BIT(pktin.body);
+ unsigned i = GET_32BIT(pktin.body);
struct ssh_channel *c;
c = find234(ssh_channels, &i, ssh_channelfind);
if (c) {
} else if (pktin.type == SSH1_SMSG_EXIT_STATUS) {
send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
} else {
- fatalbox("Strange packet received: type %d", pktin.type);
+ bombout(("Strange packet received: type %d", pktin.type));
+ crReturnV;
}
} else {
send_packet(SSH1_CMSG_STDIN_DATA,
crBegin;
random_init();
+ /*
+ * Set up the preferred cipher.
+ */
+ if (cfg.cipher == CIPHER_BLOWFISH) {
+ ciphers[0] = &ssh_blowfish_ssh2;
+ } else if (cfg.cipher == CIPHER_DES) {
+ logevent("Single DES not supported in SSH2; using 3DES");
+ ciphers[0] = &ssh_3des_ssh2;
+ } else if (cfg.cipher == CIPHER_3DES) {
+ ciphers[0] = &ssh_3des_ssh2;
+ } else {
+ /* Shouldn't happen, but we do want to initialise to _something_. */
+ ciphers[0] = &ssh_3des_ssh2;
+ }
+
begin_key_exchange:
/*
* Construct and send our key exchange packet.
* to.
*/
if (pktin.type != SSH2_MSG_KEXINIT) {
- fatalbox("expected key exchange packet from server");
+ bombout(("expected key exchange packet from server"));
+ crReturn(0);
}
kex = NULL; hostkey = NULL; cscipher_tobe = NULL; sccipher_tobe = NULL;
csmac_tobe = NULL; scmac_tobe = NULL; cscomp_tobe = NULL; sccomp_tobe = NULL;
* Currently we only support Diffie-Hellman and DSS, so let's
* bomb out if those aren't selected.
*/
- if (kex != &ssh_diffiehellman || hostkey != &ssh_dss)
- fatalbox("internal fault: chaos in SSH 2 transport layer");
+ if (kex != &ssh_diffiehellman || hostkey != &ssh_dss) {
+ bombout(("internal fault: chaos in SSH 2 transport layer"));
+ crReturn(0);
+ }
/*
* Now we begin the fun. Generate and send e for Diffie-Hellman.
crWaitUntil(ispkt);
if (pktin.type != SSH2_MSG_KEXDH_REPLY) {
- fatalbox("expected key exchange packet from server");
+ bombout(("expected key exchange packet from server"));
+ crReturn(0);
}
ssh2_pkt_getstring(&hostkeydata, &hostkeylen);
f = ssh2_pkt_getmp();
#endif
hostkey->setkey(hostkeydata, hostkeylen);
- if (!hostkey->verifysig(sigdata, siglen, exchange_hash, 20))
- fatalbox("Server failed host key check");
+ if (!hostkey->verifysig(sigdata, siglen, exchange_hash, 20)) {
+ bombout(("Server failed host key check"));
+ crReturn(0);
+ }
/*
* Expect SSH2_MSG_NEWKEYS from server.
*/
crWaitUntil(ispkt);
- if (pktin.type != SSH2_MSG_NEWKEYS)
- fatalbox("expected new-keys packet from server");
+ if (pktin.type != SSH2_MSG_NEWKEYS) {
+ bombout(("expected new-keys packet from server"));
+ crReturn(0);
+ }
/*
* Authenticate remote host: verify host key. (We've already
}
/*
- * SSH2: remote identifier for the main session channel.
- */
-static unsigned long ssh_remote_channel;
-
-/*
* Handle the SSH2 userauth and connection layers.
*/
static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
ssh2_pkt_addstring("ssh-userauth");
ssh2_pkt_send();
crWaitUntilV(ispkt);
- if (pktin.type != SSH2_MSG_SERVICE_ACCEPT)
- fatalbox("Server refused user authentication protocol");
+ if (pktin.type != SSH2_MSG_SERVICE_ACCEPT) {
+ bombout(("Server refused user authentication protocol"));
+ crReturnV;
+ }
/*
* FIXME: currently we support only password authentication.
static int pos = 0;
static char c;
- if ((flags & FLAG_CONNECTION) && !*cfg.username) {
+ if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
c_write("login as: ", 10);
+ ssh_send_ok = 1;
while (pos >= 0) {
crWaitUntilV(!ispkt);
while (inlen--) switch (c = *in++) {
char stuff[200];
strncpy(username, cfg.username, 99);
username[99] = '\0';
- if (flags & FLAG_VERBOSE) {
+ if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
sprintf(stuff, "Using username \"%s\".\r\n", username);
c_write(stuff, strlen(stuff));
}
}
- if (!(flags & FLAG_WINDOWED)) {
+ if (!(flags & FLAG_INTERACTIVE)) {
char prompt[200];
sprintf(prompt, "%s@%s's password: ", cfg.username, savedhost);
if (!ssh_get_password(prompt, password, sizeof(password))) {
}
} else {
c_write("password: ", 10);
+ ssh_send_ok = 1;
pos = 0;
while (pos >= 0) {
/*
* So now create a channel with a session in it.
*/
+ mainchan = malloc(sizeof(struct ssh_channel));
+ mainchan->localid = 100; /* as good as any */
ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
ssh2_pkt_addstring("session");
- ssh2_pkt_adduint32(100); /* as good as any */
- ssh2_pkt_adduint32(0xFFFFFFFFUL); /* very big window which we ignore */
- ssh2_pkt_adduint32(0xFFFFFFFFUL); /* very big max pkt size */
+ ssh2_pkt_adduint32(mainchan->localid);
+ ssh2_pkt_adduint32(0x7FFFFFFFUL); /* our window size */
+ ssh2_pkt_adduint32(0x4000UL); /* our max pkt size */
ssh2_pkt_send();
crWaitUntilV(ispkt);
if (pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
- fatalbox("Server refused to open a session");
+ bombout(("Server refused to open a session"));
+ crReturnV;
/* FIXME: error data comes back in FAILURE packet */
}
- if (ssh2_pkt_getuint32() != 100) {
- fatalbox("Server's channel confirmation cited wrong channel");
+ if (ssh2_pkt_getuint32() != mainchan->localid) {
+ bombout(("Server's channel confirmation cited wrong channel"));
+ crReturnV;
}
- ssh_remote_channel = ssh2_pkt_getuint32();
- remote_winsize = ssh2_pkt_getuint32();
- remote_maxpkt = ssh2_pkt_getuint32();
+ mainchan->remoteid = ssh2_pkt_getuint32();
+ mainchan->u.v2.remwindow = ssh2_pkt_getuint32();
+ mainchan->u.v2.remmaxpkt = ssh2_pkt_getuint32();
+ mainchan->u.v2.outbuffer = NULL;
+ mainchan->u.v2.outbuflen = mainchan->u.v2.outbufsize = 0;
logevent("Opened channel for session");
/*
* Now allocate a pty for the session.
*/
- ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
- ssh2_pkt_adduint32(ssh_remote_channel); /* recipient channel */
- ssh2_pkt_addstring("pty-req");
- ssh2_pkt_addbool(1); /* want reply */
- ssh2_pkt_addstring(cfg.termtype);
- ssh2_pkt_adduint32(cols);
- ssh2_pkt_adduint32(rows);
- ssh2_pkt_adduint32(0); /* pixel width */
- ssh2_pkt_adduint32(0); /* pixel height */
- ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data("\0", 1); /* TTY_OP_END, no special options */
- ssh2_pkt_send();
+ if (!cfg.nopty) {
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ ssh2_pkt_addstring("pty-req");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.termtype);
+ ssh2_pkt_adduint32(cols);
+ ssh2_pkt_adduint32(rows);
+ ssh2_pkt_adduint32(0); /* pixel width */
+ ssh2_pkt_adduint32(0); /* pixel height */
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data("\0", 1);/* TTY_OP_END, no special options */
+ ssh2_pkt_send();
- do { /* FIXME: pay attention to these */
- crWaitUntilV(ispkt);
- } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ do {
+ crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ }
+ } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
- if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
- if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
- fatalbox("Server got confused by pty request");
+ if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+ if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+ bombout(("Server got confused by pty request"));
+ crReturnV;
+ }
+ c_write("Server refused to allocate pty\r\n", 32);
+ } else {
+ logevent("Allocated pty");
}
- c_write("Server refused to allocate pty\r\n", 32);
- } else {
- logevent("Allocated pty");
}
/*
- * Start a shell.
+ * Start a shell or a remote command.
*/
ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
- ssh2_pkt_adduint32(ssh_remote_channel); /* recipient channel */
- ssh2_pkt_addstring("shell");
- ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+ if (*cfg.remote_cmd) {
+ ssh2_pkt_addstring("exec");
+ ssh2_pkt_addbool(1); /* want reply */
+ ssh2_pkt_addstring(cfg.remote_cmd);
+ } else {
+ ssh2_pkt_addstring("shell");
+ ssh2_pkt_addbool(1); /* want reply */
+ }
ssh2_pkt_send();
- do { /* FIXME: pay attention to these */
+ do {
crWaitUntilV(ispkt);
+ if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ }
} while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
- fatalbox("Server got confused by shell request");
+ bombout(("Server got confused by shell/command request"));
+ crReturnV;
}
- fatalbox("Server refused to start a shell");
+ bombout(("Server refused to start a shell/command"));
+ crReturnV;
} else {
- logevent("Started a shell");
+ logevent("Started a shell/command");
}
/*
* Transfer data!
*/
ssh_send_ok = 1;
+ begin_session();
while (1) {
+ static int try_send;
crReturnV;
+ try_send = FALSE;
if (ispkt) {
if (pktin.type == SSH2_MSG_CHANNEL_DATA ||
pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
char *data;
int length;
- if (ssh2_pkt_getuint32() != 100)
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
continue; /* wrong channel */
if (pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
ssh2_pkt_getuint32() != SSH2_EXTENDED_DATA_STDERR)
continue; /* extended but not stderr */
ssh2_pkt_getstring(&data, &length);
- if (data)
+ if (data) {
c_write(data, length);
+ /*
+ * Enlarge the window again at the remote side,
+ * just in case it ever runs down and they fail
+ * to send us any more data.
+ */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_adduint32(length);
+ ssh2_pkt_send();
+ }
} else if (pktin.type == SSH2_MSG_DISCONNECT) {
ssh_state = SSH_STATE_CLOSED;
- logevent("Received disconnect request");
+ logevent("Received disconnect message");
} else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
continue; /* exit status et al; ignore (FIXME?) */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_EOF) {
+ continue; /* remote sends EOF; ignore */
+ } else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_send();
+ /* FIXME: mark the channel as closed */
+ if (1 /* FIXME: "all channels are closed" */) {
+ logevent("All channels closed. Disconnecting");
+ ssh2_pkt_init(SSH2_MSG_DISCONNECT);
+ ssh2_pkt_send();
+ }
+ continue; /* remote sends close; ignore (FIXME) */
} else if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- continue; /* ignore for now (FIXME!) */
+ /* FIXME: be able to handle other channels here */
+ if (ssh2_pkt_getuint32() != mainchan->localid)
+ continue; /* wrong channel */
+ mainchan->u.v2.remwindow += ssh2_pkt_getuint32();
+ try_send = TRUE;
} else {
- fatalbox("Strange packet received: type %d", pktin.type);
+ bombout(("Strange packet received: type %d", pktin.type));
+ crReturnV;
}
} else {
- /* FIXME: for now, ignore window size */
- ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
- ssh2_pkt_adduint32(ssh_remote_channel);
- ssh2_pkt_addstring_start();
- ssh2_pkt_addstring_data(in, inlen);
- ssh2_pkt_send();
+ /*
+ * We have spare data. Add it to the channel buffer.
+ */
+ if (mainchan->u.v2.outbufsize <
+ mainchan->u.v2.outbuflen + inlen) {
+ mainchan->u.v2.outbufsize =
+ mainchan->u.v2.outbuflen + inlen + 1024;
+ mainchan->u.v2.outbuffer = srealloc(mainchan->u.v2.outbuffer,
+ mainchan->u.v2.outbufsize);
+ }
+ memcpy(mainchan->u.v2.outbuffer + mainchan->u.v2.outbuflen,
+ in, inlen);
+ mainchan->u.v2.outbuflen += inlen;
+ try_send = TRUE;
}
+ if (try_send) {
+ /*
+ * Try to send data on the channel if we can. (FIXME:
+ * on _all_ channels.)
+ */
+ while (mainchan->u.v2.remwindow > 0 &&
+ mainchan->u.v2.outbuflen > 0) {
+ unsigned len = mainchan->u.v2.remwindow;
+ if (len > mainchan->u.v2.outbuflen)
+ len = mainchan->u.v2.outbuflen;
+ if (len > mainchan->u.v2.remmaxpkt)
+ len = mainchan->u.v2.remmaxpkt;
+ ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
+ ssh2_pkt_adduint32(mainchan->remoteid);
+ ssh2_pkt_addstring_start();
+ ssh2_pkt_addstring_data(mainchan->u.v2.outbuffer, len);
+ ssh2_pkt_send();
+ mainchan->u.v2.outbuflen -= len;
+ memmove(mainchan->u.v2.outbuffer, mainchan->u.v2.outbuffer+len,
+ mainchan->u.v2.outbuflen);
+ mainchan->u.v2.remwindow -= len;
+ }
+ }
}
crFinishV;
if (s == INVALID_SOCKET)
return 1;
- if (WSAGETSELECTERROR(lParam) != 0)
+ if (WSAGETSELECTERROR(lParam) != 0) {
+ closesocket(s);
+ s = INVALID_SOCKET;
return -WSAGETSELECTERROR(lParam);
+ }
switch (WSAGETSELECTEVENT(lParam)) {
case FD_READ:
ret = recv(s, buf, sizeof(buf), 0);
if (ret < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
return 1;
- if (ret < 0) /* any _other_ error */
+ if (ret < 0) { /* any _other_ error */
+ closesocket(s);
+ s = INVALID_SOCKET;
return -10000-WSAGetLastError();
+ }
if (ret == 0) {
s = INVALID_SOCKET;
return 0;
*/
static void ssh_special (Telnet_Special code) {
if (code == TS_EOF) {
- if (ssh_version = 1) {
+ if (ssh_version == 1) {
send_packet(SSH1_CMSG_EOF, PKT_END);
} else {
ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
- ssh2_pkt_adduint32(ssh_remote_channel);
+ ssh2_pkt_adduint32(mainchan->remoteid);
ssh2_pkt_send();
}
+ logevent("Sent EOF message");
} else {
/* do nothing */
}
closesocket(s);
s = INVALID_SOCKET;
} else {
- fatalbox("Strange packet received: type %d", pktin.type);
+ bombout(("Strange packet received: type %d", pktin.type));
+ return 0;
}
}