summaryrefslogtreecommitdiff
path: root/hostfile.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2011-01-24 12:43:25 +0000
committerColin Watson <cjwatson@debian.org>2011-01-24 12:43:25 +0000
commit626f1d986ff72aa514da63e34744e1de9cf21b9a (patch)
treed215a5280bc2e57251e4a9e08bfd3674ad824a94 /hostfile.c
parent6ed622cb6fe8f71bbe0d998cdd12280410bfb420 (diff)
parent0970072c89b079b022538e3c366fbfa2c53fc821 (diff)
* New upstream release (http://www.openssh.org/txt/release-5.7):
- Implement Elliptic Curve Cryptography modes for key exchange (ECDH) and host/user keys (ECDSA) as specified by RFC5656. ECDH and ECDSA offer better performance than plain DH and DSA at the same equivalent symmetric key length, as well as much shorter keys. - sftp(1)/sftp-server(8): add a protocol extension to support a hard link operation. It is available through the "ln" command in the client. The old "ln" behaviour of creating a symlink is available using its "-s" option or through the preexisting "symlink" command. - scp(1): Add a new -3 option to scp: Copies between two remote hosts are transferred through the local host (closes: #508613). - ssh(1): "atomically" create the listening mux socket by binding it on a temporary name and then linking it into position after listen() has succeeded. This allows the mux clients to determine that the server socket is either ready or stale without races (closes: #454784). Stale server sockets are now automatically removed (closes: #523250). - ssh(1): install a SIGCHLD handler to reap expired child process (closes: #594687). - ssh(1)/ssh-agent(1): honour $TMPDIR for client xauth and ssh-agent temporary directories (closes: #357469, although only if you arrange for ssh-agent to actually see $TMPDIR since the setgid bit will cause it to be stripped off).
Diffstat (limited to 'hostfile.c')
-rw-r--r--hostfile.c302
1 files changed, 177 insertions, 125 deletions
diff --git a/hostfile.c b/hostfile.c
index afab6dad1..b6f924b23 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.50 2010/12/04 13:31:37 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,167 @@ 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 fclose(f);
321 return;
322}
318 323
319 if (key == NULL) { 324void
320 /* we found a key of the requested type */ 325free_hostkeys(struct hostkeys *hostkeys)
321 if (found->type == keytype) { 326{
322 fclose(f); 327 u_int i;
323 return HOST_FOUND; 328
324 } 329 for (i = 0; i < hostkeys->num_entries; i++) {
325 continue; 330 xfree(hostkeys->entries[i].host);
326 } 331 xfree(hostkeys->entries[i].file);
332 key_free(hostkeys->entries[i].key);
333 bzero(hostkeys->entries + i, sizeof(*hostkeys->entries));
334 }
335 if (hostkeys->entries != NULL)
336 xfree(hostkeys->entries);
337 hostkeys->entries = NULL;
338 hostkeys->num_entries = 0;
339 xfree(hostkeys);
340}
341
342static int
343check_key_not_revoked(struct hostkeys *hostkeys, Key *k)
344{
345 int is_cert = key_is_cert(k);
346 u_int i;
327 347
328 if (!hostfile_check_key(kbits, found, host, filename, linenum)) 348 for (i = 0; i < hostkeys->num_entries; i++) {
349 if (hostkeys->entries[i].marker != MRK_REVOKE)
329 continue; 350 continue;
351 if (key_equal_public(k, hostkeys->entries[i].key))
352 return -1;
353 if (is_cert &&
354 key_equal_public(k->cert->signature_key,
355 hostkeys->entries[i].key))
356 return -1;
357 }
358 return 0;
359}
330 360
331 if (want_revocation) { 361/*
332 if (key_is_cert(key) && 362 * Match keys against a specified key, or look one up by key type.
333 key_equal_public(key->cert->signature_key, found)) { 363 *
334 verbose("check_host_in_hostfile: revoked CA " 364 * If looking for a keytype (key == NULL) and one is found then return
335 "line %d", linenum); 365 * HOST_FOUND, otherwise HOST_NEW.
336 key_free(found); 366 *
337 return HOST_REVOKED; 367 * If looking for a key (key != NULL):
338 } 368 * 1. If the key is a cert and a matching CA is found, return HOST_OK
339 if (key_equal_public(key, found)) { 369 * 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 " 370 * 3. If no key matches but a key with a different type is found, then
341 "line %d", linenum); 371 * return HOST_CHANGED
342 key_free(found); 372 * 4. If no matching keys are found, then return HOST_NEW.
343 return HOST_REVOKED; 373 *
344 } 374 * Finally, check any found key is not revoked.
345 key_free(found); 375 */
376static HostStatus
377check_hostkeys_by_key_or_type(struct hostkeys *hostkeys,
378 Key *k, int keytype, const struct hostkey_entry **found)
379{
380 u_int i;
381 HostStatus end_return = HOST_NEW;
382 int want_cert = key_is_cert(k);
383 HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE;
384 int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2;
385
386 if (found != NULL)
387 *found = NULL;
388
389 for (i = 0; i < hostkeys->num_entries; i++) {
390 if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1)
391 continue;
392 if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1)
346 continue; 393 continue;
394 if (hostkeys->entries[i].marker != want_marker)
395 continue;
396 if (k == NULL) {
397 if (hostkeys->entries[i].key->type != keytype)
398 continue;
399 end_return = HOST_FOUND;
400 if (found != NULL)
401 *found = hostkeys->entries + i;
402 k = hostkeys->entries[i].key;
403 break;
347 } 404 }
348 405 if (want_cert) {
349 /* Check if the current key is the same as the given key. */ 406 if (key_equal_public(k->cert->signature_key,
350 if (want_cert && key_equal(key->cert->signature_key, found)) { 407 hostkeys->entries[i].key)) {
351 /* Found CA cert for key */ 408 /* A matching CA exists */
352 debug3("check_host_in_hostfile: CA match line %d", 409 end_return = HOST_OK;
353 linenum); 410 if (found != NULL)
354 fclose(f); 411 *found = hostkeys->entries + i;
355 return HOST_OK; 412 break;
356 } else if (!want_cert && key_equal(key, found)) { 413 }
357 /* Found identical key */ 414 } else {
358 debug3("check_host_in_hostfile: match line %d", linenum); 415 if (key_equal(k, hostkeys->entries[i].key)) {
359 fclose(f); 416 end_return = HOST_OK;
360 return HOST_OK; 417 if (found != NULL)
418 *found = hostkeys->entries + i;
419 break;
420 }
421 /* A non-maching key exists */
422 end_return = HOST_CHANGED;
423 if (found != NULL)
424 *found = hostkeys->entries + i;
361 } 425 }
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 } 426 }
369 /* Clear variables and close the file. */ 427 if (check_key_not_revoked(hostkeys, k) != 0) {
370 fclose(f); 428 end_return = HOST_REVOKED;
371 429 if (found != NULL)
372 /* 430 *found = NULL;
373 * Return either HOST_NEW or HOST_CHANGED, depending on whether we 431 }
374 * saw a different key for the host.
375 */
376 return end_return; 432 return end_return;
377} 433}
378 434
379HostStatus 435HostStatus
380check_host_in_hostfile(const char *filename, const char *host, const Key *key, 436check_key_in_hostkeys(struct hostkeys *hostkeys, Key *key,
381 Key *found, int *numret) 437 const struct hostkey_entry **found)
382{ 438{
383 if (key == NULL) 439 if (key == NULL)
384 fatal("no key to look up"); 440 fatal("no key to look up");
385 if (check_host_in_hostfile_by_key_or_type(filename, host, 441 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} 442}
391 443
392int 444int
393lookup_key_in_hostfile_by_type(const char *filename, const char *host, 445lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype,
394 int keytype, Key *found, int *numret) 446 const struct hostkey_entry **found)
395{ 447{
396 return (check_host_in_hostfile_by_key_or_type(filename, host, NULL, 448 return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype,
397 keytype, found, 0, numret) == HOST_FOUND); 449 found) == HOST_FOUND);
398} 450}
399 451
400/* 452/*