📄 ssh.c
字号:
}/* * Construct an SSH2 packet and add it to a deferred data block. * Useful for sending multiple packets in a single sk_write() call, * to prevent a traffic-analysing listener from being able to work * out the length of any particular packet (such as the password * packet). * * Note that because SSH2 sequence-numbers its packets, this can * NOT be used as an m4-style `defer' allowing packets to be * constructed in one order and sent in another. */static void ssh2_pkt_defer(Ssh ssh){ int len = ssh2_pkt_construct(ssh); if (ssh->deferred_len + len > ssh->deferred_size) { ssh->deferred_size = ssh->deferred_len + len + 128; ssh->deferred_send_data = sresize(ssh->deferred_send_data, ssh->deferred_size, unsigned char); } memcpy(ssh->deferred_send_data + ssh->deferred_len, ssh->pktout.data, len); ssh->deferred_len += len;}/* * Send the whole deferred data block constructed by * ssh2_pkt_defer() or SSH1's defer_packet(). */static void ssh_pkt_defersend(Ssh ssh){ int backlog; backlog = sk_write(ssh->s, (char *)ssh->deferred_send_data, ssh->deferred_len); ssh->deferred_len = ssh->deferred_size = 0; sfree(ssh->deferred_send_data); ssh->deferred_send_data = NULL; if (backlog > SSH_MAX_BACKLOG) ssh_throttle_all(ssh, 1, backlog);}#if 0void bndebug(char *string, Bignum b){ unsigned char *p; int i, len; p = ssh2_mpint_fmt(b, &len); debug(("%s", string)); for (i = 0; i < len; i++) debug((" %02x", p[i])); debug(("\n")); sfree(p);}#endifstatic void sha_mpint(SHA_State * s, Bignum b){ unsigned char *p; int len; p = ssh2_mpint_fmt(b, &len); sha_string(s, p, len); sfree(p);}/* * Packet decode functions for both SSH1 and SSH2. */static unsigned long ssh_pkt_getuint32(Ssh ssh){ unsigned long value; if (ssh->pktin.length - ssh->pktin.savedpos < 4) return 0; /* arrgh, no way to decline (FIXME?) */ value = GET_32BIT(ssh->pktin.body + ssh->pktin.savedpos); ssh->pktin.savedpos += 4; return value;}static int ssh2_pkt_getbool(Ssh ssh){ unsigned long value; if (ssh->pktin.length - ssh->pktin.savedpos < 1) return 0; /* arrgh, no way to decline (FIXME?) */ value = ssh->pktin.body[ssh->pktin.savedpos] != 0; ssh->pktin.savedpos++; return value;}static void ssh_pkt_getstring(Ssh ssh, char **p, int *length){ int len; *p = NULL; *length = 0; if (ssh->pktin.length - ssh->pktin.savedpos < 4) return; len = GET_32BIT(ssh->pktin.body + ssh->pktin.savedpos); if (len < 0) return; *length = len; ssh->pktin.savedpos += 4; if (ssh->pktin.length - ssh->pktin.savedpos < *length) return; *p = (char *)(ssh->pktin.body + ssh->pktin.savedpos); ssh->pktin.savedpos += *length;}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") || !strcmp(imp, "OSU_1.5alpha4")))) { /* * 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]; sp
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -