summaryrefslogtreecommitdiff
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
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
-rw-r--r--hostfile.c147
-rw-r--r--hostfile.h43
2 files changed, 187 insertions, 3 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}
diff --git a/hostfile.h b/hostfile.h
index d90973f42..24c3813aa 100644
--- a/hostfile.h
+++ b/hostfile.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: hostfile.h,v 1.21 2015/01/15 09:40:00 djm Exp $ */ 1/* $OpenBSD: hostfile.h,v 1.22 2015/01/18 21:40:24 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -52,4 +52,45 @@ int add_host_to_hostfile(const char *, const char *,
52 52
53char *host_hash(const char *, const char *, u_int); 53char *host_hash(const char *, const char *, u_int);
54 54
55/*
56 * Iterate through a hostkeys file, optionally parsing keys and matching
57 * hostnames. Allows access to the raw keyfile lines to allow
58 * streaming edits to the file to take place.
59 */
60#define HKF_WANT_MATCH_HOST (1) /* return only matching hosts */
61#define HKF_WANT_PARSE_KEY (1<<1) /* need key parsed */
62
63#define HKF_STATUS_OK 1 /* Line parsed, didn't match host */
64#define HKF_STATUS_INVALID 2 /* line had parse error */
65#define HKF_STATUS_COMMENT 3 /* valid line contained no key */
66#define HKF_STATUS_HOST_MATCHED 4 /* hostname matched */
67
68/*
69 * The callback function receives this as an argument for each matching
70 * hostkey line. The callback may "steal" the 'key' field by setting it to NULL.
71 * If a parse error occurred, then "hosts" and subsequent options may be NULL.
72 */
73struct hostkey_foreach_line {
74 const char *path; /* Path of file */
75 u_long linenum; /* Line number */
76 int status; /* One of HKF_STATUS_* */
77 char *line; /* Entire key line; mutable by callback */
78 int marker; /* CA/revocation markers; indicated by MRK_* value */
79 const char *hosts; /* Raw hosts text, may be hashed or list multiple */
80 int was_hashed; /* Non-zero if hostname was hashed */
81 const char *rawkey; /* Text of key and any comment following it */
82 struct sshkey *key; /* Key, if parsed ok and HKF_WANT_MATCH_HOST set */
83 const char *comment; /* Any comment following the key */
84};
85
86/*
87 * Callback fires for each line (or matching line if a HKF_WANT_* option
88 * is set). The foreach loop will terminate if the callback returns a non-
89 * zero exit status.
90 */
91typedef int hostkeys_foreach_fn(struct hostkey_foreach_line *l, void *ctx);
92
93int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx,
94 const char *host, u_int options);
95
55#endif 96#endif