summaryrefslogtreecommitdiff
path: root/hostfile.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2015-01-18 21:40:23 +0000
committerDamien Miller <djm@mindrot.org>2015-01-20 00:20:43 +1100
commitc29811cc480a260e42fd88849fc86a80c1e91038 (patch)
treeeeda69798eed42d7eba5a425288684486055f340 /hostfile.c
parentf101d8291da01bbbfd6fb8c569cfd0cc61c0d346 (diff)
upstream commit
introduce hostkeys_foreach() to allow iteration over a known_hosts file or controlled subset thereof. This will allow us to pull out some ugly and duplicated code, and will be used to implement hostkey rotation later. feedback and ok markus
Diffstat (limited to 'hostfile.c')
-rw-r--r--hostfile.c147
1 files changed, 145 insertions, 2 deletions
diff --git a/hostfile.c b/hostfile.c
index 40dbbd478..5f0366310 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.c,v 1.59 2015/01/15 09:40:00 djm Exp $ */ 1/* $OpenBSD: hostfile.c,v 1.60 2015/01/18 21:40:23 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
@@ -42,6 +42,7 @@
42 42
43#include <netinet/in.h> 43#include <netinet/in.h>
44 44
45#include <errno.h>
45#include <resolv.h> 46#include <resolv.h>
46#include <stdarg.h> 47#include <stdarg.h>
47#include <stdio.h> 48#include <stdio.h>
@@ -64,6 +65,8 @@ struct hostkeys {
64 u_int num_entries; 65 u_int num_entries;
65}; 66};
66 67
68/* XXX hmac is too easy to dictionary attack; use bcrypt? */
69
67static int 70static int
68extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len) 71extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len)
69{ 72{
@@ -496,7 +499,147 @@ add_host_to_hostfile(const char *filename, const char *host,
496 __func__, filename, ssh_err(r)); 499 __func__, filename, ssh_err(r));
497 } else 500 } else
498 success = 1; 501 success = 1;
499 fputs("\n", f); 502 fputc('\n', f);
500 fclose(f); 503 fclose(f);
501 return success; 504 return success;
502} 505}
506
507static int
508match_maybe_hashed(const char *host, const char *names, int *was_hashed)
509{
510 int hashed = *names == HASH_DELIM;
511 const char *hashed_host;
512 size_t nlen = strlen(names);
513
514 if (was_hashed != NULL)
515 *was_hashed = hashed;
516 if (hashed) {
517 if ((hashed_host = host_hash(host, names, nlen)) == NULL)
518 return -1;
519 return nlen == strlen(hashed_host) &&
520 strncmp(hashed_host, names, nlen) == 0;
521 }
522 return match_hostname(host, names, nlen) == 1;
523}
524
525int
526hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx,
527 const char *host, u_int options)
528{
529 FILE *f;
530 char line[8192], oline[8192];
531 u_long linenum = 0;
532 char *cp, *cp2;
533 u_int kbits;
534 int s, r = 0;
535 struct hostkey_foreach_line lineinfo;
536
537 memset(&lineinfo, 0, sizeof(lineinfo));
538 if (host == NULL && (options & HKF_WANT_MATCH_HOST) != 0)
539 return SSH_ERR_INVALID_ARGUMENT;
540 if ((f = fopen(path, "r")) == NULL)
541 return SSH_ERR_SYSTEM_ERROR;
542
543 debug3("%s: reading file \"%s\"", __func__, path);
544 while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) {
545 line[strcspn(line, "\n")] = '\0';
546 strlcpy(oline, line, sizeof(oline));
547
548 sshkey_free(lineinfo.key);
549 memset(&lineinfo, 0, sizeof(lineinfo));
550 lineinfo.path = path;
551 lineinfo.linenum = linenum;
552 lineinfo.line = oline;
553 lineinfo.status = HKF_STATUS_OK;
554
555 /* Skip any leading whitespace, comments and empty lines. */
556 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
557 ;
558 if (!*cp || *cp == '#' || *cp == '\n') {
559 if ((options & HKF_WANT_MATCH_HOST) == 0) {
560 lineinfo.status = HKF_STATUS_COMMENT;
561 if ((r = callback(&lineinfo, ctx)) != 0)
562 break;
563 }
564 continue;
565 }
566
567 if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) {
568 verbose("%s: invalid marker at %s:%lu",
569 __func__, path, linenum);
570 if ((options & HKF_WANT_MATCH_HOST) == 0)
571 goto bad;
572 continue;
573 }
574
575 /* Find the end of the host name portion. */
576 for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++)
577 ;
578 lineinfo.hosts = cp;
579 *cp2++ = '\0';
580
581 /* Check if the host name matches. */
582 if (host != NULL) {
583 s = match_maybe_hashed(host, lineinfo.hosts,
584 &lineinfo.was_hashed);
585 if (s == 1)
586 lineinfo.status = HKF_STATUS_HOST_MATCHED;
587 else if ((options & HKF_WANT_MATCH_HOST) != 0)
588 continue;
589 else if (s == -1) {
590 debug2("%s: %s:%ld: bad host hash \"%.32s\"",
591 __func__, path, linenum, lineinfo.hosts);
592 goto bad;
593 }
594 }
595
596 /* Got a match. Skip host name and any following whitespace */
597 for (; *cp2 == ' ' || *cp2 == '\t'; cp2++)
598 ;
599 if (*cp2 == '\0' || *cp2 == '#') {
600 debug2("%s:%ld: truncated before key", path, linenum);
601 goto bad;
602 }
603 lineinfo.rawkey = cp = cp2;
604
605 if ((options & HKF_WANT_PARSE_KEY) != 0) {
606 /*
607 * Extract the key from the line. This will skip
608 * any leading whitespace. Ignore badly formatted
609 * lines.
610 */
611 if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) {
612 error("%s: sshkey_new failed", __func__);
613 return SSH_ERR_ALLOC_FAIL;
614 }
615 if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) {
616#ifdef WITH_SSH1
617 sshkey_free(lineinfo.key);
618 lineinfo.key = sshkey_new(KEY_RSA1);
619 if (lineinfo.key == NULL) {
620 error("%s: sshkey_new fail", __func__);
621 return SSH_ERR_ALLOC_FAIL;
622 }
623 if (!hostfile_read_key(&cp, &kbits,
624 lineinfo.key))
625 goto bad;
626#else
627 goto bad;
628#endif
629 }
630 if (!hostfile_check_key(kbits, lineinfo.key, host,
631 path, linenum)) {
632 bad:
633 lineinfo.status = HKF_STATUS_INVALID;
634 if ((r = callback(&lineinfo, ctx)) != 0)
635 break;
636 continue;
637 }
638 }
639 if ((r = callback(&lineinfo, ctx)) != 0)
640 break;
641 }
642 sshkey_free(lineinfo.key);
643 fclose(f);
644 return r;
645}