X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=ssh.c;h=d395d7b5c556e72347a814cd2c8509eed27c1aa1;hb=cc66c86e7311c97db09da989c340ba3108c9e14f;hp=7e63b4e0b0460443ad835035ec5b8d1277a2c889;hpb=aaaf70a0fc0f071b9e9c6a51606c78b16a464841;p=PuTTY.git diff --git a/ssh.c b/ssh.c index 7e63b4e0..d395d7b5 100644 --- a/ssh.c +++ b/ssh.c @@ -3677,6 +3677,59 @@ static void ssh_disconnect(Ssh ssh, char *client_reason, char *wire_reason, sfree(error); } +int verify_ssh_manual_host_key(Ssh ssh, const char *fingerprint, + const struct ssh_signkey *ssh2keytype, + void *ssh2keydata) +{ + if (!conf_get_str_nthstrkey(ssh->conf, CONF_ssh_manual_hostkeys, 0)) { + return -1; /* no manual keys configured */ + } + + if (fingerprint) { + /* + * The fingerprint string we've been given will have things + * like 'ssh-rsa 2048' at the front of it. Strip those off and + * narrow down to just the colon-separated hex block at the + * end of the string. + */ + const char *p = strrchr(fingerprint, ' '); + fingerprint = p ? p+1 : fingerprint; + /* Quick sanity checks, including making sure it's in lowercase */ + assert(strlen(fingerprint) == 16*3 - 1); + assert(fingerprint[2] == ':'); + assert(fingerprint[strspn(fingerprint, "0123456789abcdef:")] == 0); + + if (conf_get_str_str_opt(ssh->conf, CONF_ssh_manual_hostkeys, + fingerprint)) + return 1; /* success */ + } + + if (ssh2keydata) { + /* + * Construct the base64-encoded public key blob and see if + * that's listed. + */ + unsigned char *binblob; + char *base64blob; + int binlen, atoms, i; + binblob = ssh2keytype->public_blob(ssh2keydata, &binlen); + atoms = (binlen + 2) / 3; + base64blob = snewn(atoms * 4 + 1, char); + for (i = 0; i < atoms; i++) + base64_encode_atom(binblob + 3*i, binlen - 3*i, base64blob + 4*i); + base64blob[atoms * 4] = '\0'; + sfree(binblob); + if (conf_get_str_str_opt(ssh->conf, CONF_ssh_manual_hostkeys, + base64blob)) { + sfree(base64blob); + return 1; /* success */ + } + sfree(base64blob); + } + + return 0; +} + /* * Handle the key exchange and user authentication phases. */ @@ -3800,29 +3853,36 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, rsastr_fmt(keystr, &s->hostkey); rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey); - ssh_set_frozen(ssh, 1); - s->dlgret = verify_ssh_host_key(ssh->frontend, - ssh->savedhost, ssh->savedport, - "rsa", keystr, fingerprint, - ssh_dialog_callback, ssh); - sfree(keystr); - if (s->dlgret < 0) { - do { - crReturn(0); - if (pktin) { - bombout(("Unexpected data from server while waiting" - " for user host key response")); - crStop(0); - } - } while (pktin || inlen > 0); - s->dlgret = ssh->user_response; - } - ssh_set_frozen(ssh, 0); + /* First check against manually configured host keys. */ + s->dlgret = verify_ssh_manual_host_key(ssh, fingerprint, NULL, NULL); + if (s->dlgret == 0) { /* did not match */ + bombout(("Host key did not appear in manually configured list")); + crStop(0); + } else if (s->dlgret < 0) { /* none configured; use standard handling */ + ssh_set_frozen(ssh, 1); + s->dlgret = verify_ssh_host_key(ssh->frontend, + ssh->savedhost, ssh->savedport, + "rsa", keystr, fingerprint, + ssh_dialog_callback, ssh); + sfree(keystr); + if (s->dlgret < 0) { + do { + crReturn(0); + if (pktin) { + bombout(("Unexpected data from server while waiting" + " for user host key response")); + crStop(0); + } + } while (pktin || inlen > 0); + s->dlgret = ssh->user_response; + } + ssh_set_frozen(ssh, 0); - if (s->dlgret == 0) { - ssh_disconnect(ssh, "User aborted at host key verification", - NULL, 0, TRUE); - crStop(0); + if (s->dlgret == 0) { + ssh_disconnect(ssh, "User aborted at host key verification", + NULL, 0, TRUE); + crStop(0); + } } } @@ -6226,6 +6286,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, pktin->savedpos += 16; /* skip garbage cookie */ ssh_pkt_getstring(pktin, &str, &len); /* key exchange algorithms */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } preferred = NULL; for (i = 0; i < s->n_preferred_kex; i++) { @@ -6245,8 +6309,8 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, break; } if (!ssh->kex) { - bombout(("Couldn't agree a key exchange algorithm (available: %s)", - str ? str : "(null)")); + bombout(("Couldn't agree a key exchange algorithm" + " (available: %.*s)", len, str)); crStopV; } /* @@ -6256,6 +6320,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, */ s->guessok = first_in_commasep_string(preferred, str, len); ssh_pkt_getstring(pktin, &str, &len); /* host key algorithms */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < lenof(hostkey_algs); i++) { if (in_commasep_string(hostkey_algs[i]->name, str, len)) { ssh->hostkey = hostkey_algs[i]; @@ -6263,14 +6331,18 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } if (!ssh->hostkey) { - bombout(("Couldn't agree a host key algorithm (available: %s)", - str ? str : "(null)")); + bombout(("Couldn't agree a host key algorithm" + " (available: %.*s)", len, str)); crStopV; } s->guessok = s->guessok && first_in_commasep_string(hostkey_algs[0]->name, str, len); ssh_pkt_getstring(pktin, &str, &len); /* client->server cipher */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) { @@ -6287,12 +6359,16 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, break; } if (!s->cscipher_tobe) { - bombout(("Couldn't agree a client-to-server cipher (available: %s)", - str ? str : "(null)")); + bombout(("Couldn't agree a client-to-server cipher" + " (available: %.*s)", len, str)); crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* server->client cipher */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < s->n_preferred_ciphers; i++) { const struct ssh2_ciphers *c = s->preferred_ciphers[i]; if (!c) { @@ -6309,12 +6385,16 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, break; } if (!s->sccipher_tobe) { - bombout(("Couldn't agree a server-to-client cipher (available: %s)", - str ? str : "(null)")); + bombout(("Couldn't agree a server-to-client cipher" + " (available: %.*s)", len, str)); crStopV; } ssh_pkt_getstring(pktin, &str, &len); /* client->server mac */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < s->nmacs; i++) { if (in_commasep_string(s->maclist[i]->name, str, len)) { s->csmac_tobe = s->maclist[i]; @@ -6322,6 +6402,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } ssh_pkt_getstring(pktin, &str, &len); /* server->client mac */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < s->nmacs; i++) { if (in_commasep_string(s->maclist[i]->name, str, len)) { s->scmac_tobe = s->maclist[i]; @@ -6329,6 +6413,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } ssh_pkt_getstring(pktin, &str, &len); /* client->server compression */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < lenof(compressions) + 1; i++) { const struct ssh_compress *c = i == 0 ? s->preferred_comp : compressions[i - 1]; @@ -6345,6 +6433,10 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, } } ssh_pkt_getstring(pktin, &str, &len); /* server->client compression */ + if (!str) { + bombout(("KEXINIT packet was incomplete")); + crStopV; + } for (i = 0; i < lenof(compressions) + 1; i++) { const struct ssh_compress *c = i == 0 ? s->preferred_comp : compressions[i - 1]; @@ -6689,31 +6781,39 @@ static void do_ssh2_transport(Ssh ssh, void *vin, int inlen, * checked the signature of the exchange hash.) */ s->fingerprint = ssh->hostkey->fingerprint(s->hkey); - ssh_set_frozen(ssh, 1); - s->dlgret = verify_ssh_host_key(ssh->frontend, - ssh->savedhost, ssh->savedport, - ssh->hostkey->keytype, s->keystr, - s->fingerprint, - ssh_dialog_callback, ssh); - if (s->dlgret < 0) { - do { - crReturnV; - if (pktin) { - bombout(("Unexpected data from server while waiting" - " for user host key response")); - crStopV; - } - } while (pktin || inlen > 0); - s->dlgret = ssh->user_response; - } - ssh_set_frozen(ssh, 0); - if (s->dlgret == 0) { - ssh_disconnect(ssh, "User aborted at host key verification", NULL, - 0, TRUE); - crStopV; - } logevent("Host key fingerprint is:"); logevent(s->fingerprint); + /* First check against manually configured host keys. */ + s->dlgret = verify_ssh_manual_host_key(ssh, s->fingerprint, + ssh->hostkey, s->hkey); + if (s->dlgret == 0) { /* did not match */ + bombout(("Host key did not appear in manually configured list")); + crStopV; + } else if (s->dlgret < 0) { /* none configured; use standard handling */ + ssh_set_frozen(ssh, 1); + s->dlgret = verify_ssh_host_key(ssh->frontend, + ssh->savedhost, ssh->savedport, + ssh->hostkey->keytype, s->keystr, + s->fingerprint, + ssh_dialog_callback, ssh); + if (s->dlgret < 0) { + do { + crReturnV; + if (pktin) { + bombout(("Unexpected data from server while waiting" + " for user host key response")); + crStopV; + } + } while (pktin || inlen > 0); + s->dlgret = ssh->user_response; + } + ssh_set_frozen(ssh, 0); + if (s->dlgret == 0) { + ssh_disconnect(ssh, "Aborted at host key verification", NULL, + 0, TRUE); + crStopV; + } + } sfree(s->fingerprint); /* * Save this host key, to check against the one presented in @@ -10525,11 +10625,13 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle, ssh->gsslibs = NULL; #endif + random_ref(); /* do this now - may be needed by sharing setup code */ + p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive); - if (p != NULL) + if (p != NULL) { + random_unref(); return p; - - random_ref(); + } return NULL; }