📄 ssh.c
字号:
static void *ssh_pkt_getdata(Ssh ssh, int length)
{
if (ssh->pktin.length - ssh->pktin.savedpos < length)
return NULL;
ssh->pktin.savedpos += length;
return ssh->pktin.body + (ssh->pktin.savedpos - length);
}
static int ssh1_pkt_getrsakey(Ssh ssh, struct RSAKey *key,
unsigned char **keystr)
{
int j;
j = makekey(ssh->pktin.body + ssh->pktin.savedpos,
ssh->pktin.length - ssh->pktin.savedpos,
key, keystr, 0);
if (j < 0)
return FALSE;
ssh->pktin.savedpos += j;
assert(ssh->pktin.savedpos < ssh->pktin.length);
return TRUE;
}
static Bignum ssh1_pkt_getmp(Ssh ssh)
{
int j;
Bignum b;
j = ssh1_read_bignum(ssh->pktin.body + ssh->pktin.savedpos,
ssh->pktin.length - ssh->pktin.savedpos, &b);
if (j < 0)
return NULL;
ssh->pktin.savedpos += j;
return b;
}
static Bignum ssh2_pkt_getmp(Ssh ssh)
{
char *p;
int length;
Bignum b;
ssh_pkt_getstring(ssh, &p, &length);
if (!p)
return NULL;
if (p[0] & 0x80) {
bombout(("internal error: Can't handle negative mpints"));
return NULL;
}
b = bignum_from_bytes((unsigned char *)p, length);
return b;
}
/*
* Helper function to add an SSH2 signature blob to a packet.
* Expects to be shown the public key blob as well as the signature
* blob. Normally works just like ssh2_pkt_addstring, but will
* fiddle with the signature packet if necessary for
* BUG_SSH2_RSA_PADDING.
*/
static void ssh2_add_sigblob(Ssh ssh, void *pkblob_v, int pkblob_len,
void *sigblob_v, int sigblob_len)
{
unsigned char *pkblob = (unsigned char *)pkblob_v;
unsigned char *sigblob = (unsigned char *)sigblob_v;
/* dmemdump(pkblob, pkblob_len); */
/* dmemdump(sigblob, sigblob_len); */
/*
* See if this is in fact an ssh-rsa signature and a buggy
* server; otherwise we can just do this the easy way.
*/
if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) &&
(GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {
int pos, len, siglen;
/*
* Find the byte length of the modulus.
*/
pos = 4+7; /* skip over "ssh-rsa" */
pos += 4 + GET_32BIT(pkblob+pos); /* skip over exponent */
len = GET_32BIT(pkblob+pos); /* find length of modulus */
pos += 4; /* find modulus itself */
while (len > 0 && pkblob[pos] == 0)
len--, pos++;
/* debug(("modulus length is %d\n", len)); */
/*
* Now find the signature integer.
*/
pos = 4+7; /* skip over "ssh-rsa" */
siglen = GET_32BIT(sigblob+pos);
/* debug(("signature length is %d\n", siglen)); */
if (len != siglen) {
unsigned char newlen[4];
ssh2_pkt_addstring_start(ssh);
ssh2_pkt_addstring_data(ssh, (char *)sigblob, pos);
/* dmemdump(sigblob, pos); */
pos += 4; /* point to start of actual sig */
PUT_32BIT(newlen, len);
ssh2_pkt_addstring_data(ssh, (char *)newlen, 4);
/* dmemdump(newlen, 4); */
newlen[0] = 0;
while (len-- > siglen) {
ssh2_pkt_addstring_data(ssh, (char *)newlen, 1);
/* dmemdump(newlen, 1); */
}
ssh2_pkt_addstring_data(ssh, (char *)(sigblob+pos), siglen);
/* dmemdump(sigblob+pos, siglen); */
return;
}
/* Otherwise fall through and do it the easy way. */
}
ssh2_pkt_addstring_start(ssh);
ssh2_pkt_addstring_data(ssh, (char *)sigblob, sigblob_len);
}
/*
* Examine the remote side's version string and compare it against
* a list of known buggy implementations.
*/
static void ssh_detect_bugs(Ssh ssh, char *vstring)
{
char *imp; /* pointer to implementation part */
imp = vstring;
imp += strcspn(imp, "-");
if (*imp) imp++;
imp += strcspn(imp, "-");
if (*imp) imp++;
ssh->remote_bugs = 0;
if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
(ssh->cfg.sshbug_ignore1 == AUTO &&
(!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
!strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
!strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25") ||
!strcmp(imp, "OSU_1.4alpha3")))) {
/*
* These versions don't support SSH1_MSG_IGNORE, so we have
* to use a different defence against password length
* sniffing.
*/
ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
logevent("We believe remote version has SSH1 ignore bug");
}
if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
(ssh->cfg.sshbug_plainpw1 == AUTO &&
(!strcmp(imp, "Cisco-1.25") || !strcmp(imp, "OSU_1.4alpha3")))) {
/*
* These versions need a plain password sent; they can't
* handle having a null and a random length of data after
* the password.
*/
ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
logevent("We believe remote version needs a plain SSH1 password");
}
if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
(ssh->cfg.sshbug_rsa1 == AUTO &&
(!strcmp(imp, "Cisco-1.25")))) {
/*
* These versions apparently have no clue whatever about
* RSA authentication and will panic and die if they see
* an AUTH_RSA message.
*/
ssh->remote_bugs |= BUG_CHOKES_ON_RSA;
logevent("We believe remote version can't handle RSA authentication");
}
if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
(ssh->cfg.sshbug_hmac2 == AUTO &&
!wc_match("* VShell", imp) &&
(wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
wc_match("2.1 *", imp)))) {
/*
* These versions have the HMAC bug.
*/
ssh->remote_bugs |= BUG_SSH2_HMAC;
logevent("We believe remote version has SSH2 HMAC bug");
}
if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
(ssh->cfg.sshbug_derivekey2 == AUTO &&
!wc_match("* VShell", imp) &&
(wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
/*
* These versions have the key-derivation bug (failing to
* include the literal shared secret in the hashes that
* generate the keys).
*/
ssh->remote_bugs |= BUG_SSH2_DERIVEKEY;
logevent("We believe remote version has SSH2 key-derivation bug");
}
if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
(ssh->cfg.sshbug_rsapad2 == AUTO &&
(wc_match("OpenSSH_2.[5-9]*", imp) ||
wc_match("OpenSSH_3.[0-2]*", imp)))) {
/*
* These versions have the SSH2 RSA padding bug.
*/
ssh->remote_bugs |= BUG_SSH2_RSA_PADDING;
logevent("We believe remote version has SSH2 RSA padding bug");
}
if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||
(ssh->cfg.sshbug_pksessid2 == AUTO &&
wc_match("OpenSSH_2.[0-2]*", imp))) {
/*
* These versions have the SSH2 session-ID bug in
* public-key authentication.
*/
ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
logevent("We believe remote version has SSH2 public-key-session-ID bug");
}
if (ssh->cfg.sshbug_dhgex2 == FORCE_ON) {
/*
* User specified the SSH2 DH GEX bug.
*/
ssh->remote_bugs |= BUG_SSH2_DH_GEX;
logevent("We believe remote version has SSH2 DH group exchange bug");
}
}
static int do_ssh_init(Ssh ssh, unsigned char c)
{
struct do_ssh_init_state {
int vslen;
char version[10];
char *vstring;
int vstrsize;
int i;
int proto1, proto2;
};
crState(do_ssh_init_state);
crBegin(ssh->do_ssh_init_crstate);
/* Search for the string "SSH-" in the input. */
s->i = 0;
while (1) {
static const int transS[] = { 1, 2, 2, 1 };
static const int transH[] = { 0, 0, 3, 0 };
static const int transminus[] = { 0, 0, 0, -1 };
if (c == 'S')
s->i = transS[s->i];
else if (c == 'H')
s->i = transH[s->i];
else if (c == '-')
s->i = transminus[s->i];
else
s->i = 0;
if (s->i < 0)
break;
crReturn(1); /* get another character */
}
s->vstrsize = 16;
s->vstring = snewn(s->vstrsize, char);
strcpy(s->vstring, "SSH-");
s->vslen = 4;
s->i = 0;
while (1) {
crReturn(1); /* get another char */
if (s->vslen >= s->vstrsize - 1) {
s->vstrsize += 16;
s->vstring = sresize(s->vstring, s->vstrsize, char);
}
s->vstring[s->vslen++] = c;
if (s->i >= 0) {
if (c == '-') {
s->version[s->i] = '\0';
s->i = -1;
} else if (s->i < sizeof(s->version) - 1)
s->version[s->i++] = c;
} else if (c == '\012')
break;
}
ssh->agentfwd_enabled = FALSE;
ssh->rdpkt2_state.incoming_sequence = 0;
s->vstring[s->vslen] = 0;
s->vstring[strcspn(s->vstring, "\r\n")] = '\0';/* remove EOL chars */
{
char *vlog;
vlog = snewn(20 + s->vslen, char);
sprintf(vlog, "Server version: %s", s->vstring);
logevent(vlog);
sfree(vlog);
}
ssh_detect_bugs(ssh, s->vstring);
/*
* Decide which SSH protocol version to support.
*/
/* Anything strictly below "2.0" means protocol 1 is supported. */
s->proto1 = ssh_versioncmp(s->version, "2.0") < 0;
/* Anything greater or equal to "1.99" means protocol 2 is supported. */
s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0;
if (ssh->cfg.sshprot == 0 && !s->proto1) {
bombout(("SSH protocol version 1 required by user but not provided by server"));
crStop(0);
}
if (ssh->cfg.sshprot == 3 && !s->proto2) {
bombout(("SSH protocol version 2 required by user but not provided by server"));
crStop(0);
}
if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) {
/*
* Use v2 protocol.
*/
char verstring[80], vlog[100];
sprintf(verstring, "SSH-2.0-%s", sshver);
SHA_Init(&ssh->exhashbase);
/*
* Hash our version string and their version string.
*/
sha_string(&ssh->exhashbase, verstring, strlen(verstring));
sha_string(&ssh->exhashbase, s->vstring, strcspn(s->vstring, "\r\n"));
sprintf(vlog, "We claim version: %s", verstring);
logevent(vlog);
strcat(verstring, "\012");
logevent("Using SSH protocol version 2");
sk_write(ssh->s, verstring, strlen(verstring));
ssh->protocol = ssh2_protocol;
ssh->version = 2;
ssh->s_rdpkt = ssh2_rdpkt;
} else {
/*
* Use v1 protocol.
*/
char verstring[80], vlog[100];
sprintf(verstring, "SSH-%s-%s",
(ssh_versioncmp(s->version, "1.5") <= 0 ? s->version : "1.5"),
sshver);
sprintf(vlog, "We claim version: %s", verstring);
logevent(vlog);
strcat(verstring, "\012");
logevent("Using SSH protocol version 1");
sk_write(ssh->s, verstring, strlen(verstring));
ssh->protocol = ssh1_protocol;
ssh->version = 1;
ssh->s_rdpkt = ssh1_rdpkt;
}
update_specials_menu(ssh->frontend);
ssh->state = SSH_STATE_BEFORE_SIZE;
sfree(s->vstring);
crFinish(0);
}
static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
{
crBegin(ssh->ssh_gotdata_crstate);
/*
* To begin with, feed the characters one by one to the
* protocol initialisation / selection function do_ssh_init().
* When that returns 0, we're done with the initial greeting
* exchange and can move on to packet discipline.
*/
while (1) {
int ret; /* need not be kept across crReturn */
if (datalen == 0)
crReturnV; /* more data please */
ret = do_ssh_init(ssh, *data);
data++;
datalen--;
if (ret == 0)
break;
}
/*
* We emerge from that loop when the initial negotiation is
* over and we have selected an s_rdpkt function. Now pass
* everything to s_rdpkt, and then pass the resulting packets
* to the proper protocol handler.
*/
if (datalen == 0)
crReturnV;
while (1) {
while (datalen > 0) {
if (ssh->s_rdpkt(ssh, &data, &datalen) == 0) {
if (ssh->state == SSH_STATE_CLOSED) {
return;
}
ssh->protocol(ssh, NULL, 0, 1);
if (ssh->state == SSH_STATE_CLOSED) {
return;
}
}
}
crReturnV;
}
crFinishV;
}
static void ssh_do_close(Ssh ssh)
{
int i;
struct ssh_channel *c;
ssh->state =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -