summaryrefslogtreecommitdiff
path: root/hostfile.c
diff options
context:
space:
mode:
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/*