From a465ea2d49539af1d2575498a5ce45e51980d982 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Sat, 3 Feb 2018 16:38:11 +0100 Subject: [PATCH] knownhosts: Add ssh_known_hosts_read_entries() Signed-off-by: Andreas Schneider --- src/knownhosts.c | 87 ++++++++++++++++++++ tests/unittests/torture_knownhosts_parsing.c | 30 +++++++ 2 files changed, 117 insertions(+) diff --git a/src/knownhosts.c b/src/knownhosts.c index 6557f89f..cf353ef5 100644 --- a/src/knownhosts.c +++ b/src/knownhosts.c @@ -142,6 +142,93 @@ void ssh_knownhosts_entry_free(struct ssh_knownhosts_entry *entry) SAFE_FREE(entry); } +static int known_hosts_read_line(FILE *fp, + char *buf, + size_t buf_size, + size_t *buf_len, + size_t *lineno) +{ + while (fgets(buf, buf_size, fp) != NULL) { + size_t len; + if (buf[0] == '\0') { + continue; + } + + *lineno += 1; + len = strlen(buf); + if (buf_len != NULL) { + *buf_len = len; + } + if (buf[len - 1] == '\n' || feof(fp)) { + return 0; + } else { + errno = E2BIG; + return -1; + } + } + + return -1; +} + +static int ssh_known_hosts_read_entries(const char *match, + const char *filename, + struct ssh_list **entries) +{ + struct ssh_list *entry_list; + char line[8192]; + size_t lineno = 0; + size_t len = 0; + FILE *fp; + int rc; + + fp = fopen(filename, "r"); + if (fp == NULL) { + return SSH_ERROR; + } + + entry_list = ssh_list_new(); + if (entry_list == NULL) { + return SSH_ERROR; + } + + for (rc = known_hosts_read_line(fp, line, sizeof(line), &len, &lineno); + rc == 0; + rc = known_hosts_read_line(fp, line, sizeof(line), &len, &lineno)) { + struct ssh_knownhosts_entry *entry = NULL; + char *p; + + if (line[len] != '\n') { + len = strcspn(line, "\n"); + } + line[len] = '\0'; + + /* Skip leading spaces */ + for (p = line; isspace((int)p[0]); p++); + + /* Skip comments and empty lines */ + if (p[0] == '\0' || p[0] == '#') { + continue; + } + + rc = ssh_known_hosts_parse_line(match, + line, + &entry); + if (rc == SSH_AGAIN) { + continue; + } else if (rc != SSH_OK) { + goto error; + } + ssh_list_append(entry_list, entry); + } + + *entries = entry_list; + + return SSH_OK; +error: + ssh_list_free(entry_list); + return SSH_ERROR; +} + /** * @brief Parse a line from a known_hosts entry into a structure * diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c index 8955c7e8..51d816f2 100644 --- a/tests/unittests/torture_knownhosts_parsing.c +++ b/tests/unittests/torture_knownhosts_parsing.c @@ -199,6 +199,33 @@ static void torture_knownhosts_parse_line_hashed_ed25519(void **state) { SSH_KNOWNHOSTS_ENTRY_FREE(entry); } +static void torture_knownhosts_read_file(void **state) +{ + const char *knownhosts_file = *state; + struct ssh_list *entry_list = NULL; + struct ssh_iterator *it = NULL; + int rc; + + rc = ssh_known_hosts_read_entries("localhost", + knownhosts_file, + &entry_list); + assert_int_equal(rc, SSH_OK); + assert_non_null(entry_list); + it = ssh_list_get_iterator(entry_list); + assert_non_null(it); + for (;it != NULL; it = it->next) { + struct ssh_knownhosts_entry *entry = NULL; + enum ssh_keytypes_e type; + + entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it); + assert_non_null(entry); + + assert_string_equal(entry->hostname, "localhost"); + type = ssh_key_type(entry->publickey); + assert_int_equal(type, SSH_KEYTYPE_ED25519); + } +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -208,6 +235,9 @@ int torture_run_tests(void) { cmocka_unit_test(torture_knownhosts_parse_line_port_ed25519), cmocka_unit_test(torture_knownhosts_parse_line_pattern_ed25519), cmocka_unit_test(torture_knownhosts_parse_line_hashed_ed25519), + cmocka_unit_test_setup_teardown(torture_knownhosts_read_file, + setup_knownhosts_file, + teardown_knownhosts_file), }; ssh_init();