summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2010-12-01 12:21:51 +1100
committerDamien Miller <djm@mindrot.org>2010-12-01 12:21:51 +1100
commitd925dcd8a5d1a3070061006788352bed93260582 (patch)
tree12f78195086ff506d0f4e4c39098d675cdae0ee9
parent03c0e533de56a1fc55ec1885d35c3197fdefbf94 (diff)
- djm@cvs.openbsd.org 2010/11/29 23:45:51
[auth.c hostfile.c hostfile.h ssh.c ssh_config.5 sshconnect.c] [sshconnect.h sshconnect2.c] automatically order the hostkeys requested by the client based on which hostkeys are already recorded in known_hosts. This avoids hostkey warnings when connecting to servers with new ECDSA keys that are preferred by default; with markus@
-rw-r--r--ChangeLog7
-rw-r--r--auth.c30
-rw-r--r--hostfile.c301
-rw-r--r--hostfile.h30
-rw-r--r--ssh.c4
-rw-r--r--ssh_config.57
-rw-r--r--sshconnect.c291
-rw-r--r--sshconnect.h11
-rw-r--r--sshconnect2.c62
9 files changed, 452 insertions, 291 deletions
diff --git a/ChangeLog b/ChangeLog
index 44e45eb8a..6ee7c0014 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -38,6 +38,13 @@
38 [authfile.c] 38 [authfile.c]
39 correctly load comment for encrypted rsa1 keys; 39 correctly load comment for encrypted rsa1 keys;
40 report/fix Joachim Schipper; ok djm@ 40 report/fix Joachim Schipper; ok djm@
41 - djm@cvs.openbsd.org 2010/11/29 23:45:51
42 [auth.c hostfile.c hostfile.h ssh.c ssh_config.5 sshconnect.c]
43 [sshconnect.h sshconnect2.c]
44 automatically order the hostkeys requested by the client based on
45 which hostkeys are already recorded in known_hosts. This avoids
46 hostkey warnings when connecting to servers with new ECDSA keys
47 that are preferred by default; with markus@
41 48
4220101124 4920101124
43 - (dtucker) [platform.c session.c] Move the getluid call out of session.c and 50 - (dtucker) [platform.c session.c] Move the getluid call out of session.c and
diff --git a/auth.c b/auth.c
index 6fe1b21a4..33680b91b 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.c,v 1.90 2010/11/23 02:35:50 djm Exp $ */ 1/* $OpenBSD: auth.c,v 1.91 2010/11/29 23:45:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * 4 *
@@ -379,16 +379,15 @@ HostStatus
379check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, 379check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
380 const char *sysfile, const char *userfile) 380 const char *sysfile, const char *userfile)
381{ 381{
382 Key *found;
383 char *user_hostfile; 382 char *user_hostfile;
384 struct stat st; 383 struct stat st;
385 HostStatus host_status; 384 HostStatus host_status;
385 struct hostkeys *hostkeys;
386 const struct hostkey_entry *found;
386 387
387 /* Check if we know the host and its host key. */ 388 hostkeys = init_hostkeys();
388 found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); 389 load_hostkeys(hostkeys, host, sysfile);
389 host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); 390 if (userfile != NULL) {
390
391 if (host_status != HOST_OK && userfile != NULL) {
392 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); 391 user_hostfile = tilde_expand_filename(userfile, pw->pw_uid);
393 if (options.strict_modes && 392 if (options.strict_modes &&
394 (stat(user_hostfile, &st) == 0) && 393 (stat(user_hostfile, &st) == 0) &&
@@ -401,16 +400,23 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
401 user_hostfile); 400 user_hostfile);
402 } else { 401 } else {
403 temporarily_use_uid(pw); 402 temporarily_use_uid(pw);
404 host_status = check_host_in_hostfile(user_hostfile, 403 load_hostkeys(hostkeys, host, user_hostfile);
405 host, key, found, NULL);
406 restore_uid(); 404 restore_uid();
407 } 405 }
408 xfree(user_hostfile); 406 xfree(user_hostfile);
409 } 407 }
410 key_free(found); 408 host_status = check_key_in_hostkeys(hostkeys, key, &found);
409 if (host_status == HOST_REVOKED)
410 error("WARNING: revoked key for %s attempted authentication",
411 found->host);
412 else if (host_status == HOST_OK)
413 debug("%s: key for %s found at %s:%ld", __func__,
414 found->host, found->file, found->line);
415 else
416 debug("%s: key for host %s not found", __func__, host);
417
418 free_hostkeys(hostkeys);
411 419
412 debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ?
413 "ok" : "not found", host);
414 return host_status; 420 return host_status;
415} 421}
416 422
diff --git a/hostfile.c b/hostfile.c
index afab6dad1..9145529cb 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.c,v 1.48 2010/03/04 10:36:03 djm Exp $ */ 1/* $OpenBSD: hostfile.c,v 1.49 2010/11/29 23:45:51 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -56,6 +56,12 @@
56#include "key.h" 56#include "key.h"
57#include "hostfile.h" 57#include "hostfile.h"
58#include "log.h" 58#include "log.h"
59#include "misc.h"
60
61struct hostkeys {
62 struct hostkey_entry *entries;
63 u_int num_entries;
64};
59 65
60static int 66static int
61extract_salt(const char *s, u_int l, char *salt, size_t salt_len) 67extract_salt(const char *s, u_int l, char *salt, size_t salt_len)
@@ -164,26 +170,28 @@ hostfile_read_key(char **cpp, u_int *bitsp, Key *ret)
164 170
165 /* Return results. */ 171 /* Return results. */
166 *cpp = cp; 172 *cpp = cp;
167 *bitsp = key_size(ret); 173 if (bitsp != NULL)
174 *bitsp = key_size(ret);
168 return 1; 175 return 1;
169} 176}
170 177
171static int 178static int
172hostfile_check_key(int bits, const Key *key, const char *host, const char *filename, int linenum) 179hostfile_check_key(int bits, const Key *key, const char *host,
180 const char *filename, u_long linenum)
173{ 181{
174 if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL) 182 if (key == NULL || key->type != KEY_RSA1 || key->rsa == NULL)
175 return 1; 183 return 1;
176 if (bits != BN_num_bits(key->rsa->n)) { 184 if (bits != BN_num_bits(key->rsa->n)) {
177 logit("Warning: %s, line %d: keysize mismatch for host %s: " 185 logit("Warning: %s, line %lu: keysize mismatch for host %s: "
178 "actual %d vs. announced %d.", 186 "actual %d vs. announced %d.",
179 filename, linenum, host, BN_num_bits(key->rsa->n), bits); 187 filename, linenum, host, BN_num_bits(key->rsa->n), bits);
180 logit("Warning: replace %d with %d in %s, line %d.", 188 logit("Warning: replace %d with %d in %s, line %lu.",
181 bits, BN_num_bits(key->rsa->n), filename, linenum); 189 bits, BN_num_bits(key->rsa->n), filename, linenum);
182 } 190 }
183 return 1; 191 return 1;
184} 192}
185 193
186static enum { MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA } 194static HostkeyMarker
187check_markers(char **cpp) 195check_markers(char **cpp)
188{ 196{
189 char marker[32], *sp, *cp = *cpp; 197 char marker[32], *sp, *cp = *cpp;
@@ -218,49 +226,32 @@ check_markers(char **cpp)
218 return ret; 226 return ret;
219} 227}
220 228
221/* 229struct hostkeys *
222 * Checks whether the given host (which must be in all lowercase) is already 230init_hostkeys(void)
223 * in the list of our known hosts. Returns HOST_OK if the host is known and 231{
224 * has the specified key, HOST_NEW if the host is not known, and HOST_CHANGED 232 struct hostkeys *ret = xcalloc(1, sizeof(*ret));
225 * if the host is known but used to have a different host key.
226 *
227 * If no 'key' has been specified and a key of type 'keytype' is known
228 * for the specified host, then HOST_FOUND is returned.
229 */
230 233
231static HostStatus 234 ret->entries = NULL;
232check_host_in_hostfile_by_key_or_type(const char *filename, 235 return ret;
233 const char *host, const Key *key, int keytype, Key *found, 236}
234 int want_revocation, int *numret) 237
238void
239load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path)
235{ 240{
236 FILE *f; 241 FILE *f;
237 char line[8192]; 242 char line[8192];
238 int want, have, linenum = 0, want_cert = key_is_cert(key); 243 u_long linenum = 0, num_loaded = 0;
239 u_int kbits;
240 char *cp, *cp2, *hashed_host; 244 char *cp, *cp2, *hashed_host;
241 HostStatus end_return; 245 HostkeyMarker marker;
242 246 Key *key;
243 debug3("check_host_in_hostfile: host %s filename %s", host, filename); 247 int kbits;
244 248
245 if (want_revocation && (key == NULL || keytype != 0 || found != NULL)) 249 if ((f = fopen(path, "r")) == NULL)
246 fatal("%s: invalid arguments", __func__); 250 return;
247 251 debug3("%s: loading entries for host \"%.100s\" from file \"%s\"",
248 /* Open the file containing the list of known hosts. */ 252 __func__, host, path);
249 f = fopen(filename, "r"); 253 while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
250 if (!f)
251 return HOST_NEW;
252
253 /*
254 * Return value when the loop terminates. This is set to
255 * HOST_CHANGED if we have seen a different key for the host and have
256 * not found the proper one.
257 */
258 end_return = HOST_NEW;
259
260 /* Go through the file. */
261 while (fgets(line, sizeof(line), f)) {
262 cp = line; 254 cp = line;
263 linenum++;
264 255
265 /* Skip any leading whitespace, comments and empty lines. */ 256 /* Skip any leading whitespace, comments and empty lines. */
266 for (; *cp == ' ' || *cp == '\t'; cp++) 257 for (; *cp == ' ' || *cp == '\t'; cp++)
@@ -268,19 +259,11 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
268 if (!*cp || *cp == '#' || *cp == '\n') 259 if (!*cp || *cp == '#' || *cp == '\n')
269 continue; 260 continue;
270 261
271 if (want_revocation) 262 if ((marker = check_markers(&cp)) == MRK_ERROR) {
272 want = MRK_REVOKE; 263 verbose("%s: invalid marker at %s:%lu",
273 else if (want_cert) 264 __func__, path, linenum);
274 want = MRK_CA;
275 else
276 want = MRK_NONE;
277
278 if ((have = check_markers(&cp)) == MRK_ERROR) {
279 verbose("%s: invalid marker at %s:%d",
280 __func__, filename, linenum);
281 continue;
282 } else if (want != have)
283 continue; 265 continue;
266 }
284 267
285 /* Find the end of the host name portion. */ 268 /* Find the end of the host name portion. */
286 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) 269 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
@@ -292,8 +275,8 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
292 continue; 275 continue;
293 hashed_host = host_hash(host, cp, (u_int) (cp2 - cp)); 276 hashed_host = host_hash(host, cp, (u_int) (cp2 - cp));
294 if (hashed_host == NULL) { 277 if (hashed_host == NULL) {
295 debug("Invalid hashed host line %d of %s", 278 debug("Invalid hashed host line %lu of %s",
296 linenum, filename); 279 linenum, path);
297 continue; 280 continue;
298 } 281 }
299 if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0) 282 if (strncmp(hashed_host, cp, (u_int) (cp2 - cp)) != 0)
@@ -303,98 +286,166 @@ check_host_in_hostfile_by_key_or_type(const char *filename,
303 /* Got a match. Skip host name. */ 286 /* Got a match. Skip host name. */
304 cp = cp2; 287 cp = cp2;
305 288
306 if (want_revocation)
307 found = key_new(KEY_UNSPEC);
308
309 /* 289 /*
310 * Extract the key from the line. This will skip any leading 290 * Extract the key from the line. This will skip any leading
311 * whitespace. Ignore badly formatted lines. 291 * whitespace. Ignore badly formatted lines.
312 */ 292 */
313 if (!hostfile_read_key(&cp, &kbits, found)) 293 key = key_new(KEY_UNSPEC);
294 if (!hostfile_read_key(&cp, &kbits, key)) {
295 key_free(key);
296 key = key_new(KEY_RSA1);
297 if (!hostfile_read_key(&cp, &kbits, key)) {
298 key_free(key);
299 continue;
300 }
301 }
302 if (!hostfile_check_key(kbits, key, host, path, linenum))
314 continue; 303 continue;
315 304
316 if (numret != NULL) 305 debug3("%s: found %skey type %s in file %s:%lu", __func__,
317 *numret = linenum; 306 marker == MRK_NONE ? "" :
307 (marker == MRK_CA ? "ca " : "revoked "),
308 key_type(key), path, linenum);
309 hostkeys->entries = xrealloc(hostkeys->entries,
310 hostkeys->num_entries + 1, sizeof(*hostkeys->entries));
311 hostkeys->entries[hostkeys->num_entries].host = xstrdup(host);
312 hostkeys->entries[hostkeys->num_entries].file = xstrdup(path);
313 hostkeys->entries[hostkeys->num_entries].line = linenum;
314 hostkeys->entries[hostkeys->num_entries].key = key;
315 hostkeys->entries[hostkeys->num_entries].marker = marker;
316 hostkeys->num_entries++;
317 num_loaded++;
318 }
319 debug3("%s: loaded %lu keys", __func__, num_loaded);
320 return;
321}
318 322
319 if (key == NULL) { 323void
320 /* we found a key of the requested type */ 324free_hostkeys(struct hostkeys *hostkeys)
321 if (found->type == keytype) { 325{
322 fclose(f); 326 u_int i;
323 return HOST_FOUND; 327
324 } 328 for (i = 0; i < hostkeys->num_entries; i++) {
325 continue; 329 xfree(hostkeys->entries[i].host);
326 } 330 xfree(hostkeys->entries[i].file);
331 key_free(hostkeys->entries[i].key);
332 bzero(hostkeys->entries + i, sizeof(*hostkeys->entries));
333 }
334 if (hostkeys->entries != NULL)
335 xfree(hostkeys->entries);
336 hostkeys->entries = NULL;
337 hostkeys->num_entries = 0;
338 xfree(hostkeys);
339}
327 340
328 if (!hostfile_check_key(kbits, found, host, filename, linenum)) 341static int
342check_key_not_revoked(struct hostkeys *hostkeys, Key *k)
343{
344 int is_cert = key_is_cert(k);
345 u_int i;
346
347 for (i = 0; i < hostkeys->num_entries; i++) {
348 if (hostkeys->entries[i].marker != MRK_REVOKE)
329 continue; 349 continue;
350 if (key_equal_public(k, hostkeys->entries[i].key))
351 return -1;
352 if (is_cert &&
353 key_equal_public(k->cert->signature_key,
354 hostkeys->entries[i].key))
355 return -1;
356 }
357 return 0;
358}
330 359
331 if (want_revocation) { 360/*
332 if (key_is_cert(key) && 361 * Match keys against a specified key, or look one up by key type.
333 key_equal_public(key->cert->signature_key, found)) { 362 *
334 verbose("check_host_in_hostfile: revoked CA " 363 * If looking for a keytype (key == NULL) and one is found then return
335 "line %d", linenum); 364 * HOST_FOUND, otherwise HOST_NEW.
336 key_free(found); 365 *
337 return HOST_REVOKED; 366 * If looking for a key (key != NULL):
338 } 367 * 1. If the key is a cert and a matching CA is found, return HOST_OK
339 if (key_equal_public(key, found)) { 368 * 2. If the key is not a cert and a matching key is found, return HOST_OK
340 verbose("check_host_in_hostfile: revoked key " 369 * 3. If no key matches but a key with a different type is found, then
341 "line %d", linenum); 370 * return HOST_CHANGED
342 key_free(found); 371 * 4. If no matching keys are found, then return HOST_NEW.
343 return HOST_REVOKED; 372 *
344 } 373 * Finally, check any found key is not revoked.
345 key_free(found); 374 */
375static HostStatus
376check_hostkeys_by_key_or_type(struct hostkeys *hostkeys,
377 Key *k, int keytype, const struct hostkey_entry **found)
378{
379 u_int i;
380 HostStatus end_return = HOST_NEW;
381 int want_cert = key_is_cert(k);
382 HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE;
383 int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2;
384
385 if (found != NULL)
386 *found = NULL;
387
388 for (i = 0; i < hostkeys->num_entries; i++) {
389 if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1)
390 continue;
391 if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1)
346 continue; 392 continue;
393 if (hostkeys->entries[i].marker != want_marker)
394 continue;
395 if (k == NULL) {
396 if (hostkeys->entries[i].key->type != keytype)
397 continue;
398 end_return = HOST_FOUND;
399 if (found != NULL)
400 *found = hostkeys->entries + i;
401 k = hostkeys->entries[i].key;
402 break;
347 } 403 }
348 404 if (want_cert) {
349 /* Check if the current key is the same as the given key. */ 405 if (key_equal_public(k->cert->signature_key,
350 if (want_cert && key_equal(key->cert->signature_key, found)) { 406 hostkeys->entries[i].key)) {
351 /* Found CA cert for key */ 407 /* A matching CA exists */
352 debug3("check_host_in_hostfile: CA match line %d", 408 end_return = HOST_OK;
353 linenum); 409 if (found != NULL)
354 fclose(f); 410 *found = hostkeys->entries + i;
355 return HOST_OK; 411 break;
356 } else if (!want_cert && key_equal(key, found)) { 412 }
357 /* Found identical key */ 413 } else {
358 debug3("check_host_in_hostfile: match line %d", linenum); 414 if (key_equal(k, hostkeys->entries[i].key)) {
359 fclose(f); 415 end_return = HOST_OK;
360 return HOST_OK; 416 if (found != NULL)
417 *found = hostkeys->entries + i;
418 break;
419 }
420 /* A non-maching key exists */
421 end_return = HOST_CHANGED;
422 if (found != NULL)
423 *found = hostkeys->entries + i;
361 } 424 }
362 /*
363 * They do not match. We will continue to go through the
364 * file; however, we note that we will not return that it is
365 * new.
366 */
367 end_return = HOST_CHANGED;
368 } 425 }
369 /* Clear variables and close the file. */ 426 if (check_key_not_revoked(hostkeys, k) != 0) {
370 fclose(f); 427 end_return = HOST_REVOKED;
371 428 if (found != NULL)
372 /* 429 *found = NULL;
373 * Return either HOST_NEW or HOST_CHANGED, depending on whether we 430 }
374 * saw a different key for the host.
375 */
376 return end_return; 431 return end_return;
377} 432}
378 433
379HostStatus 434HostStatus
380check_host_in_hostfile(const char *filename, const char *host, const Key *key, 435check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key,
381 Key *found, int *numret) 436 const struct hostkey_entry **found)
382{ 437{
383 if (key == NULL) 438 if (key == NULL)
384 fatal("no key to look up"); 439 fatal("no key to look up");
385 if (check_host_in_hostfile_by_key_or_type(filename, host, 440 return check_hostkeys_by_key_or_type(hostkeys, key, 0, found);
386 key, 0, NULL, 1, NULL) == HOST_REVOKED)
387 return HOST_REVOKED;
388 return check_host_in_hostfile_by_key_or_type(filename, host, key, 0,
389 found, 0, numret);
390} 441}
391 442
392int 443int
393lookup_key_in_hostfile_by_type(const char *filename, const char *host, 444lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
394 int keytype, Key *found, int *numret) 445 const struct hostkey_entry **found)
395{ 446{
396 return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, 447 return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
397 keytype, found, 0, numret) == HOST_FOUND); 448 found) == HOST_FOUND);
398} 449}
399 450
400/* 451/*
diff --git a/hostfile.h b/hostfile.h
index 1d460c1a9..d84d422ff 100644
--- a/hostfile.h
+++ b/hostfile.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.h,v 1.18 2010/03/04 10:36:03 djm Exp $ */ 1/* $OpenBSD: hostfile.h,v 1.19 2010/11/29 23:45:51 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -18,12 +18,30 @@ typedef enum {
18 HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND 18 HOST_OK, HOST_NEW, HOST_CHANGED, HOST_REVOKED, HOST_FOUND
19} HostStatus; 19} HostStatus;
20 20
21typedef enum {
22 MRK_ERROR, MRK_NONE, MRK_REVOKE, MRK_CA
23} HostkeyMarker;
24
25struct hostkey_entry {
26 char *host;
27 char *file;
28 u_long line;
29 Key *key;
30 HostkeyMarker marker;
31};
32struct hostkeys;
33
34struct hostkeys *init_hostkeys(void);
35void load_hostkeys(struct hostkeys *, const char *, const char *);
36void free_hostkeys(struct hostkeys *);
37
38HostStatus check_key_in_hostkeys(struct hostkeys *, Key *,
39 const struct hostkey_entry **);
40int lookup_key_in_hostkeys_by_type(struct hostkeys *, int,
41 const struct hostkey_entry **);
42
21int hostfile_read_key(char **, u_int *, Key *); 43int hostfile_read_key(char **, u_int *, Key *);
22HostStatus check_host_in_hostfile(const char *, const char *, 44int add_host_to_hostfile(const char *, const char *, const Key *, int);
23 const Key *, Key *, int *);
24int add_host_to_hostfile(const char *, const char *, const Key *, int);
25int lookup_key_in_hostfile_by_type(const char *, const char *,
26 int, Key *, int *);
27 45
28#define HASH_MAGIC "|1|" 46#define HASH_MAGIC "|1|"
29#define HASH_DELIM '|' 47#define HASH_DELIM '|'
diff --git a/ssh.c b/ssh.c
index f413f8a5c..ec690ae38 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.354 2010/11/13 23:27:50 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.355 2010/11/29 23:45:51 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -880,7 +880,7 @@ main(int ac, char **av)
880 880
881 /* Log into the remote system. Never returns if the login fails. */ 881 /* Log into the remote system. Never returns if the login fails. */
882 ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, 882 ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr,
883 pw, timeout_ms); 883 options.port, pw, timeout_ms);
884 884
885 if (packet_connection_is_on_socket()) { 885 if (packet_connection_is_on_socket()) {
886 verbose("Authenticated to %s ([%s]:%d).", host, 886 verbose("Authenticated to %s ([%s]:%d).", host,
diff --git a/ssh_config.5 b/ssh_config.5
index a51a37dde..5c6673de3 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: ssh_config.5,v 1.144 2010/11/15 07:40:14 jmc Exp $ 36.\" $OpenBSD: ssh_config.5,v 1.145 2010/11/29 23:45:51 djm Exp $
37.Dd $Mdocdate: November 15 2010 $ 37.Dd $Mdocdate: November 29 2010 $
38.Dt SSH_CONFIG 5 38.Dt SSH_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -555,6 +555,9 @@ ssh-rsa-cert-v00@openssh.com,ssh-dss-cert-v00@openssh.com,
555ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, 555ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
556ssh-rsa,ssh-dss 556ssh-rsa,ssh-dss
557.Ed 557.Ed
558.Pp
559If hostkeys are known for the destination host then this default is modified
560to prefer their algorithms.
558.It Cm HostKeyAlias 561.It Cm HostKeyAlias
559Specifies an alias that should be used instead of the 562Specifies an alias that should be used instead of the
560real host name when looking up or saving the host key 563real host name when looking up or saving the host key
diff --git a/sshconnect.c b/sshconnect.c
index 78068c602..064bb74b3 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect.c,v 1.228 2010/10/06 21:10:21 djm Exp $ */ 1/* $OpenBSD: sshconnect.c,v 1.229 2010/11/29 23:45:51 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -75,7 +75,7 @@ extern char *__progname;
75extern uid_t original_real_uid; 75extern uid_t original_real_uid;
76extern uid_t original_effective_uid; 76extern uid_t original_effective_uid;
77 77
78static int show_other_keys(const char *, Key *); 78static int show_other_keys(struct hostkeys *, Key *);
79static void warn_changed_key(Key *); 79static void warn_changed_key(Key *);
80 80
81/* 81/*
@@ -607,6 +607,79 @@ check_host_cert(const char *host, const Key *host_key)
607 return 1; 607 return 1;
608} 608}
609 609
610static int
611sockaddr_is_local(struct sockaddr *hostaddr)
612{
613 switch (hostaddr->sa_family) {
614 case AF_INET:
615 return (ntohl(((struct sockaddr_in *)hostaddr)->
616 sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
617 case AF_INET6:
618 return IN6_IS_ADDR_LOOPBACK(
619 &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
620 default:
621 return 0;
622 }
623}
624
625/*
626 * Prepare the hostname and ip address strings that are used to lookup
627 * host keys in known_hosts files. These may have a port number appended.
628 */
629void
630get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr,
631 u_short port, char **hostfile_hostname, char **hostfile_ipaddr)
632{
633 char ntop[NI_MAXHOST];
634 socklen_t addrlen;
635
636 switch (hostaddr == NULL ? -1 : hostaddr->sa_family) {
637 case -1:
638 addrlen = 0;
639 break;
640 case AF_INET:
641 addrlen = sizeof(struct sockaddr_in);
642 break;
643 case AF_INET6:
644 addrlen = sizeof(struct sockaddr_in6);
645 break;
646 default:
647 addrlen = sizeof(struct sockaddr);
648 break;
649 }
650
651 /*
652 * We don't have the remote ip-address for connections
653 * using a proxy command
654 */
655 if (hostfile_ipaddr != NULL) {
656 if (options.proxy_command == NULL) {
657 if (getnameinfo(hostaddr, addrlen,
658 ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0)
659 fatal("check_host_key: getnameinfo failed");
660 *hostfile_ipaddr = put_host_port(ntop, port);
661 } else {
662 *hostfile_ipaddr = xstrdup("<no hostip for proxy "
663 "command>");
664 }
665 }
666
667 /*
668 * Allow the user to record the key under a different name or
669 * differentiate a non-standard port. This is useful for ssh
670 * tunneling over forwarded connections or if you run multiple
671 * sshd's on different ports on the same machine.
672 */
673 if (hostfile_hostname != NULL) {
674 if (options.host_key_alias != NULL) {
675 *hostfile_hostname = xstrdup(options.host_key_alias);
676 debug("using hostkeyalias: %s", *hostfile_hostname);
677 } else {
678 *hostfile_hostname = put_host_port(hostname, port);
679 }
680 }
681}
682
610/* 683/*
611 * check whether the supplied host key is valid, return -1 if the key 684 * check whether the supplied host key is valid, return -1 if the key
612 * is not valid. the user_hostfile will not be updated if 'readonly' is true. 685 * is not valid. the user_hostfile will not be updated if 'readonly' is true.
@@ -616,21 +689,21 @@ check_host_cert(const char *host, const Key *host_key)
616#define ROQUIET 2 689#define ROQUIET 2
617static int 690static int
618check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, 691check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
619 Key *host_key, int readonly, const char *user_hostfile, 692 Key *host_key, int readonly, char *user_hostfile,
620 const char *system_hostfile) 693 char *system_hostfile)
621{ 694{
622 Key *file_key, *raw_key = NULL; 695 Key *raw_key = NULL;
623 const char *type; 696 const char *type;
624 char *ip = NULL, *host = NULL; 697 char *ip = NULL, *host = NULL;
625 char hostline[1000], *hostp, *fp, *ra; 698 char hostline[1000], *hostp, *fp, *ra;
626 HostStatus host_status; 699 HostStatus host_status;
627 HostStatus ip_status; 700 HostStatus ip_status;
628 int r, want_cert, local = 0, host_ip_differ = 0; 701 int r, want_cert = key_is_cert(host_key), host_ip_differ = 0;
629 int salen; 702 int local = sockaddr_is_local(hostaddr);
630 char ntop[NI_MAXHOST];
631 char msg[1024]; 703 char msg[1024];
632 int len, host_line, ip_line, cancelled_forwarding = 0; 704 int len, cancelled_forwarding = 0;
633 const char *host_file = NULL, *ip_file = NULL; 705 struct hostkeys *host_hostkeys, *ip_hostkeys;
706 const struct hostkey_entry *host_found, *ip_found;
634 707
635 /* 708 /*
636 * Force accepting of the host key for loopback/localhost. The 709 * Force accepting of the host key for loopback/localhost. The
@@ -640,23 +713,6 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
640 * essentially disables host authentication for localhost; however, 713 * essentially disables host authentication for localhost; however,
641 * this is probably not a real problem. 714 * this is probably not a real problem.
642 */ 715 */
643 /** hostaddr == 0! */
644 switch (hostaddr->sa_family) {
645 case AF_INET:
646 local = (ntohl(((struct sockaddr_in *)hostaddr)->
647 sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
648 salen = sizeof(struct sockaddr_in);
649 break;
650 case AF_INET6:
651 local = IN6_IS_ADDR_LOOPBACK(
652 &(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
653 salen = sizeof(struct sockaddr_in6);
654 break;
655 default:
656 local = 0;
657 salen = sizeof(struct sockaddr_storage);
658 break;
659 }
660 if (options.no_host_authentication_for_localhost == 1 && local && 716 if (options.no_host_authentication_for_localhost == 1 && local &&
661 options.host_key_alias == NULL) { 717 options.host_key_alias == NULL) {
662 debug("Forcing accepting of host key for " 718 debug("Forcing accepting of host key for "
@@ -665,17 +721,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
665 } 721 }
666 722
667 /* 723 /*
668 * We don't have the remote ip-address for connections 724 * Prepare the hostname and address strings used for hostkey lookup.
669 * using a proxy command 725 * In some cases, these will have a port number appended.
670 */ 726 */
671 if (options.proxy_command == NULL) { 727 get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);
672 if (getnameinfo(hostaddr, salen, ntop, sizeof(ntop),
673 NULL, 0, NI_NUMERICHOST) != 0)
674 fatal("check_host_key: getnameinfo failed");
675 ip = put_host_port(ntop, port);
676 } else {
677 ip = xstrdup("<no hostip for proxy command>");
678 }
679 728
680 /* 729 /*
681 * Turn off check_host_ip if the connection is to localhost, via proxy 730 * Turn off check_host_ip if the connection is to localhost, via proxy
@@ -685,74 +734,52 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
685 strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) 734 strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
686 options.check_host_ip = 0; 735 options.check_host_ip = 0;
687 736
688 /* 737 host_hostkeys = init_hostkeys();
689 * Allow the user to record the key under a different name or 738 load_hostkeys(host_hostkeys, host, user_hostfile);
690 * differentiate a non-standard port. This is useful for ssh 739 load_hostkeys(host_hostkeys, host, system_hostfile);
691 * tunneling over forwarded connections or if you run multiple 740
692 * sshd's on different ports on the same machine. 741 ip_hostkeys = NULL;
693 */ 742 if (!want_cert && options.check_host_ip) {
694 if (options.host_key_alias != NULL) { 743 ip_hostkeys = init_hostkeys();
695 host = xstrdup(options.host_key_alias); 744 load_hostkeys(ip_hostkeys, ip, user_hostfile);
696 debug("using hostkeyalias: %s", host); 745 load_hostkeys(ip_hostkeys, ip, system_hostfile);
697 } else {
698 host = put_host_port(hostname, port);
699 } 746 }
700 747
701 retry: 748 retry:
749 /* Reload these as they may have changed on cert->key downgrade */
702 want_cert = key_is_cert(host_key); 750 want_cert = key_is_cert(host_key);
703 type = key_type(host_key); 751 type = key_type(host_key);
704 752
705 /* 753 /*
706 * Store the host key from the known host file in here so that we can
707 * compare it with the key for the IP address.
708 */
709 file_key = key_new(key_is_cert(host_key) ? KEY_UNSPEC : host_key->type);
710
711 /*
712 * Check if the host key is present in the user's list of known 754 * Check if the host key is present in the user's list of known
713 * hosts or in the systemwide list. 755 * hosts or in the systemwide list.
714 */ 756 */
715 host_file = user_hostfile; 757 host_status = check_key_in_hostkeys(host_hostkeys, host_key,
716 host_status = check_host_in_hostfile(host_file, host, host_key, 758 &host_found);
717 file_key, &host_line); 759
718 if (host_status == HOST_NEW) {
719 host_file = system_hostfile;
720 host_status = check_host_in_hostfile(host_file, host, host_key,
721 file_key, &host_line);
722 }
723 /* 760 /*
724 * Also perform check for the ip address, skip the check if we are 761 * Also perform check for the ip address, skip the check if we are
725 * localhost, looking for a certificate, or the hostname was an ip 762 * localhost, looking for a certificate, or the hostname was an ip
726 * address to begin with. 763 * address to begin with.
727 */ 764 */
728 if (!want_cert && options.check_host_ip) { 765 if (!want_cert && ip_hostkeys != NULL) {
729 Key *ip_key = key_new(host_key->type); 766 ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
730 767 &ip_found);
731 ip_file = user_hostfile;
732 ip_status = check_host_in_hostfile(ip_file, ip, host_key,
733 ip_key, &ip_line);
734 if (ip_status == HOST_NEW) {
735 ip_file = system_hostfile;
736 ip_status = check_host_in_hostfile(ip_file, ip,
737 host_key, ip_key, &ip_line);
738 }
739 if (host_status == HOST_CHANGED && 768 if (host_status == HOST_CHANGED &&
740 (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) 769 (ip_status != HOST_CHANGED ||
770 (ip_found != NULL &&
771 !key_equal(ip_found->key, host_found->key))))
741 host_ip_differ = 1; 772 host_ip_differ = 1;
742
743 key_free(ip_key);
744 } else 773 } else
745 ip_status = host_status; 774 ip_status = host_status;
746 775
747 key_free(file_key);
748
749 switch (host_status) { 776 switch (host_status) {
750 case HOST_OK: 777 case HOST_OK:
751 /* The host is known and the key matches. */ 778 /* The host is known and the key matches. */
752 debug("Host '%.200s' is known and matches the %s host %s.", 779 debug("Host '%.200s' is known and matches the %s host %s.",
753 host, type, want_cert ? "certificate" : "key"); 780 host, type, want_cert ? "certificate" : "key");
754 debug("Found %s in %s:%d", 781 debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
755 want_cert ? "CA key" : "key", host_file, host_line); 782 host_found->file, host_found->line);
756 if (want_cert && !check_host_cert(hostname, host_key)) 783 if (want_cert && !check_host_cert(hostname, host_key))
757 goto fail; 784 goto fail;
758 if (options.check_host_ip && ip_status == HOST_NEW) { 785 if (options.check_host_ip && ip_status == HOST_NEW) {
@@ -803,7 +830,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
803 } else if (options.strict_host_key_checking == 2) { 830 } else if (options.strict_host_key_checking == 2) {
804 char msg1[1024], msg2[1024]; 831 char msg1[1024], msg2[1024];
805 832
806 if (show_other_keys(host, host_key)) 833 if (show_other_keys(host_hostkeys, host_key))
807 snprintf(msg1, sizeof(msg1), 834 snprintf(msg1, sizeof(msg1),
808 "\nbut keys of different type are already" 835 "\nbut keys of different type are already"
809 " known for this host."); 836 " known for this host.");
@@ -844,8 +871,7 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
844 * local known_hosts file. 871 * local known_hosts file.
845 */ 872 */
846 if (options.check_host_ip && ip_status == HOST_NEW) { 873 if (options.check_host_ip && ip_status == HOST_NEW) {
847 snprintf(hostline, sizeof(hostline), "%s,%s", 874 snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
848 host, ip);
849 hostp = hostline; 875 hostp = hostline;
850 if (options.hash_known_hosts) { 876 if (options.hash_known_hosts) {
851 /* Add hash of host and IP separately */ 877 /* Add hash of host and IP separately */
@@ -899,8 +925,8 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
899 * all hosts that one might visit. 925 * all hosts that one might visit.
900 */ 926 */
901 debug("Host certificate authority does not " 927 debug("Host certificate authority does not "
902 "match %s in %s:%d", CA_MARKER, 928 "match %s in %s:%lu", CA_MARKER,
903 host_file, host_line); 929 host_found->file, host_found->line);
904 goto fail; 930 goto fail;
905 } 931 }
906 if (readonly == ROQUIET) 932 if (readonly == ROQUIET)
@@ -922,13 +948,15 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
922 error("DNS SPOOFING is happening or the IP address for the host"); 948 error("DNS SPOOFING is happening or the IP address for the host");
923 error("and its host key have changed at the same time."); 949 error("and its host key have changed at the same time.");
924 if (ip_status != HOST_NEW) 950 if (ip_status != HOST_NEW)
925 error("Offending key for IP in %s:%d", ip_file, ip_line); 951 error("Offending key for IP in %s:%lu",
952 ip_found->file, ip_found->line);
926 } 953 }
927 /* The host key has changed. */ 954 /* The host key has changed. */
928 warn_changed_key(host_key); 955 warn_changed_key(host_key);
929 error("Add correct host key in %.100s to get rid of this message.", 956 error("Add correct host key in %.100s to get rid of this message.",
930 user_hostfile); 957 user_hostfile);
931 error("Offending key in %s:%d", host_file, host_line); 958 error("Offending %s key in %s:%lu", key_type(host_found->key),
959 host_found->file, host_found->line);
932 960
933 /* 961 /*
934 * If strict host key checking is in use, the user will have 962 * If strict host key checking is in use, the user will have
@@ -1013,13 +1041,13 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
1013 snprintf(msg, sizeof(msg), 1041 snprintf(msg, sizeof(msg),
1014 "Warning: the %s host key for '%.200s' " 1042 "Warning: the %s host key for '%.200s' "
1015 "differs from the key for the IP address '%.128s'" 1043 "differs from the key for the IP address '%.128s'"
1016 "\nOffending key for IP in %s:%d", 1044 "\nOffending key for IP in %s:%lu",
1017 type, host, ip, ip_file, ip_line); 1045 type, host, ip, ip_found->file, ip_found->line);
1018 if (host_status == HOST_OK) { 1046 if (host_status == HOST_OK) {
1019 len = strlen(msg); 1047 len = strlen(msg);
1020 snprintf(msg + len, sizeof(msg) - len, 1048 snprintf(msg + len, sizeof(msg) - len,
1021 "\nMatching host key in %s:%d", 1049 "\nMatching host key in %s:%lu",
1022 host_file, host_line); 1050 host_found->file, host_found->line);
1023 } 1051 }
1024 if (options.strict_host_key_checking == 1) { 1052 if (options.strict_host_key_checking == 1) {
1025 logit("%s", msg); 1053 logit("%s", msg);
@@ -1037,6 +1065,10 @@ check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
1037 1065
1038 xfree(ip); 1066 xfree(ip);
1039 xfree(host); 1067 xfree(host);
1068 if (host_hostkeys != NULL)
1069 free_hostkeys(host_hostkeys);
1070 if (ip_hostkeys != NULL)
1071 free_hostkeys(ip_hostkeys);
1040 return 0; 1072 return 0;
1041 1073
1042fail: 1074fail:
@@ -1056,6 +1088,10 @@ fail:
1056 key_free(raw_key); 1088 key_free(raw_key);
1057 xfree(ip); 1089 xfree(ip);
1058 xfree(host); 1090 xfree(host);
1091 if (host_hostkeys != NULL)
1092 free_hostkeys(host_hostkeys);
1093 if (ip_hostkeys != NULL)
1094 free_hostkeys(ip_hostkeys);
1059 return -1; 1095 return -1;
1060} 1096}
1061 1097
@@ -1065,6 +1101,11 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
1065{ 1101{
1066 struct stat st; 1102 struct stat st;
1067 int flags = 0; 1103 int flags = 0;
1104 char *fp;
1105
1106 fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
1107 debug("Server host key: %s %s", key_type(host_key), fp);
1108 xfree(fp);
1068 1109
1069 /* XXX certs are not yet supported for DNS */ 1110 /* XXX certs are not yet supported for DNS */
1070 if (!key_is_cert(host_key) && options.verify_host_key_dns && 1111 if (!key_is_cert(host_key) && options.verify_host_key_dns &&
@@ -1108,7 +1149,7 @@ verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
1108 */ 1149 */
1109void 1150void
1110ssh_login(Sensitive *sensitive, const char *orighost, 1151ssh_login(Sensitive *sensitive, const char *orighost,
1111 struct sockaddr *hostaddr, struct passwd *pw, int timeout_ms) 1152 struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms)
1112{ 1153{
1113 char *host, *cp; 1154 char *host, *cp;
1114 char *server_user, *local_user; 1155 char *server_user, *local_user;
@@ -1131,7 +1172,7 @@ ssh_login(Sensitive *sensitive, const char *orighost,
1131 /* key exchange */ 1172 /* key exchange */
1132 /* authenticate user */ 1173 /* authenticate user */
1133 if (compat20) { 1174 if (compat20) {
1134 ssh_kex2(host, hostaddr); 1175 ssh_kex2(host, hostaddr, port);
1135 ssh_userauth2(local_user, server_user, host, sensitive); 1176 ssh_userauth2(local_user, server_user, host, sensitive);
1136 } else { 1177 } else {
1137 ssh_kex(host, hostaddr); 1178 ssh_kex(host, hostaddr);
@@ -1158,61 +1199,35 @@ ssh_put_password(char *password)
1158 xfree(padded); 1199 xfree(padded);
1159} 1200}
1160 1201
1161static int
1162show_key_from_file(const char *file, const char *host, int keytype)
1163{
1164 Key *found;
1165 char *fp, *ra;
1166 int line, ret;
1167
1168 found = key_new(keytype);
1169 if ((ret = lookup_key_in_hostfile_by_type(file, host,
1170 keytype, found, &line))) {
1171 fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
1172 ra = key_fingerprint(found, SSH_FP_MD5, SSH_FP_RANDOMART);
1173 logit("WARNING: %s key found for host %s\n"
1174 "in %s:%d\n"
1175 "%s key fingerprint %s.\n%s\n",
1176 key_type(found), host, file, line,
1177 key_type(found), fp, ra);
1178 xfree(ra);
1179 xfree(fp);
1180 }
1181 key_free(found);
1182 return (ret);
1183}
1184
1185/* print all known host keys for a given host, but skip keys of given type */ 1202/* print all known host keys for a given host, but skip keys of given type */
1186static int 1203static int
1187show_other_keys(const char *host, Key *key) 1204show_other_keys(struct hostkeys *hostkeys, Key *key)
1188{ 1205{
1189 int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1}; 1206 int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, -1};
1190 int i, found = 0; 1207 int i, ret = 0;
1208 char *fp, *ra;
1209 const struct hostkey_entry *found;
1191 1210
1192 for (i = 0; type[i] != -1; i++) { 1211 for (i = 0; type[i] != -1; i++) {
1193 if (type[i] == key->type) 1212 if (type[i] == key->type)
1194 continue; 1213 continue;
1195 if (type[i] != KEY_RSA1 && 1214 if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found))
1196 show_key_from_file(options.user_hostfile2, host, type[i])) {
1197 found = 1;
1198 continue;
1199 }
1200 if (type[i] != KEY_RSA1 &&
1201 show_key_from_file(options.system_hostfile2, host, type[i])) {
1202 found = 1;
1203 continue;
1204 }
1205 if (show_key_from_file(options.user_hostfile, host, type[i])) {
1206 found = 1;
1207 continue; 1215 continue;
1208 } 1216 fp = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_HEX);
1209 if (show_key_from_file(options.system_hostfile, host, type[i])) { 1217 ra = key_fingerprint(found->key, SSH_FP_MD5, SSH_FP_RANDOMART);
1210 found = 1; 1218 logit("WARNING: %s key found for host %s\n"
1211 continue; 1219 "in %s:%lu\n"
1212 } 1220 "%s key fingerprint %s.",
1213 debug2("no key of type %d for host %s", type[i], host); 1221 key_type(found->key),
1222 found->host, found->file, found->line,
1223 key_type(found->key), fp);
1224 if (options.visual_host_key)
1225 logit("%s", ra);
1226 xfree(ra);
1227 xfree(fp);
1228 ret = 1;
1214 } 1229 }
1215 return (found); 1230 return ret;
1216} 1231}
1217 1232
1218static void 1233static void
diff --git a/sshconnect.h b/sshconnect.h
index 69163afbc..fd7f7f7c6 100644
--- a/sshconnect.h
+++ b/sshconnect.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect.h,v 1.26 2010/10/06 06:39:28 djm Exp $ */ 1/* $OpenBSD: sshconnect.h,v 1.27 2010/11/29 23:45:51 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -36,15 +36,18 @@ ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
36 int *, int, int, const char *); 36 int *, int, int, const char *);
37void ssh_kill_proxy_command(void); 37void ssh_kill_proxy_command(void);
38 38
39void 39void ssh_login(Sensitive *, const char *, struct sockaddr *, u_short,
40ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *, int); 40 struct passwd *, int);
41 41
42void ssh_exchange_identification(int); 42void ssh_exchange_identification(int);
43 43
44int verify_host_key(char *, struct sockaddr *, Key *); 44int verify_host_key(char *, struct sockaddr *, Key *);
45 45
46void get_hostfile_hostname_ipaddr(char *, struct sockaddr *, u_short,
47 char **, char **);
48
46void ssh_kex(char *, struct sockaddr *); 49void ssh_kex(char *, struct sockaddr *);
47void ssh_kex2(char *, struct sockaddr *); 50void ssh_kex2(char *, struct sockaddr *, u_short);
48 51
49void ssh_userauth1(const char *, const char *, char *, Sensitive *); 52void ssh_userauth1(const char *, const char *, char *, Sensitive *);
50void ssh_userauth2(const char *, const char *, char *, Sensitive *); 53void ssh_userauth2(const char *, const char *, char *, Sensitive *);
diff --git a/sshconnect2.c b/sshconnect2.c
index 6fe356cca..3cb9b101c 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect2.c,v 1.185 2010/09/22 05:01:29 djm Exp $ */ 1/* $OpenBSD: sshconnect2.c,v 1.186 2010/11/29 23:45:51 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2008 Damien Miller. All rights reserved. 4 * Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -69,6 +69,7 @@
69#include "msg.h" 69#include "msg.h"
70#include "pathnames.h" 70#include "pathnames.h"
71#include "uidswap.h" 71#include "uidswap.h"
72#include "hostfile.h"
72#include "schnorr.h" 73#include "schnorr.h"
73#include "jpake.h" 74#include "jpake.h"
74 75
@@ -101,8 +102,60 @@ verify_host_key_callback(Key *hostkey)
101 return 0; 102 return 0;
102} 103}
103 104
105static char *
106order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port)
107{
108 char *oavail, *avail, *first, *last, *alg, *hostname, *ret;
109 size_t maxlen;
110 struct hostkeys *hostkeys;
111 int ktype;
112
113 /* Find all hostkeys for this hostname */
114 get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL);
115 hostkeys = init_hostkeys();
116 load_hostkeys(hostkeys, hostname, options.user_hostfile2);
117 load_hostkeys(hostkeys, hostname, options.system_hostfile2);
118 load_hostkeys(hostkeys, hostname, options.user_hostfile);
119 load_hostkeys(hostkeys, hostname, options.system_hostfile);
120
121 oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG);
122 maxlen = strlen(avail) + 1;
123 first = xmalloc(maxlen);
124 last = xmalloc(maxlen);
125 *first = *last = '\0';
126
127#define ALG_APPEND(to, from) \
128 do { \
129 if (*to != '\0') \
130 strlcat(to, ",", maxlen); \
131 strlcat(to, from, maxlen); \
132 } while (0)
133
134 while ((alg = strsep(&avail, ",")) && *alg != '\0') {
135 if ((ktype = key_type_from_name(alg)) == KEY_UNSPEC)
136 fatal("%s: unknown alg %s", __func__, alg);
137 if (lookup_key_in_hostkeys_by_type(hostkeys,
138 key_type_plain(ktype), NULL))
139 ALG_APPEND(first, alg);
140 else
141 ALG_APPEND(last, alg);
142 }
143#undef ALG_APPEND
144 xasprintf(&ret, "%s%s%s", first, *first == '\0' ? "" : ",", last);
145 if (*first != '\0')
146 debug3("%s: prefer hostkeyalgs: %s", __func__, first);
147
148 xfree(first);
149 xfree(last);
150 xfree(hostname);
151 xfree(oavail);
152 free_hostkeys(hostkeys);
153
154 return ret;
155}
156
104void 157void
105ssh_kex2(char *host, struct sockaddr *hostaddr) 158ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port)
106{ 159{
107 Kex *kex; 160 Kex *kex;
108 161
@@ -135,6 +188,11 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
135 if (options.hostkeyalgorithms != NULL) 188 if (options.hostkeyalgorithms != NULL)
136 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = 189 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
137 options.hostkeyalgorithms; 190 options.hostkeyalgorithms;
191 else {
192 /* Prefer algorithms that we already have keys for */
193 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
194 order_hostkeyalgs(host, hostaddr, port);
195 }
138 if (options.kex_algorithms != NULL) 196 if (options.kex_algorithms != NULL)
139 myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms; 197 myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
140 198