Blame src/knownhosts.c

Packit 6c0a39
/*
Packit 6c0a39
 * known_hosts: Host and public key verification.
Packit 6c0a39
 *
Packit 6c0a39
 * This file is part of the SSH Library
Packit 6c0a39
 *
Packit 6c0a39
 * Copyright (c) 2003-2009 by Aris Adamantiadis
Packit 6c0a39
 * Copyright (c) 2009-2017 by Andreas Schneider <asn@cryptomilk.org>
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is free software; you can redistribute it and/or modify
Packit 6c0a39
 * it under the terms of the GNU Lesser General Public License as published by
Packit 6c0a39
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit 6c0a39
 * option) any later version.
Packit 6c0a39
 *
Packit 6c0a39
 * The SSH Library is distributed in the hope that it will be useful, but
Packit 6c0a39
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 6c0a39
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit 6c0a39
 * License for more details.
Packit 6c0a39
 *
Packit 6c0a39
 * You should have received a copy of the GNU Lesser General Public License
Packit 6c0a39
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit 6c0a39
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit 6c0a39
 * MA 02111-1307, USA.
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
#include "config.h"
Packit 6c0a39
Packit 6c0a39
#include <ctype.h>
Packit 6c0a39
#include <errno.h>
Packit 6c0a39
#include <stdio.h>
Packit 6c0a39
#include <stdlib.h>
Packit 6c0a39
Packit 6c0a39
#ifndef _WIN32
Packit 6c0a39
#include <arpa/inet.h>
Packit 6c0a39
#include <netinet/in.h>
Packit 6c0a39
#endif
Packit 6c0a39
Packit 6c0a39
#include "libssh/priv.h"
Packit 6c0a39
#include "libssh/dh.h"
Packit 6c0a39
#include "libssh/session.h"
Packit 6c0a39
#include "libssh/options.h"
Packit 6c0a39
#include "libssh/misc.h"
Packit 6c0a39
#include "libssh/pki.h"
Packit 6c0a39
#include "libssh/dh.h"
Packit 6c0a39
#include "libssh/knownhosts.h"
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @addtogroup libssh_session
Packit 6c0a39
 *
Packit 6c0a39
 * @{
Packit 6c0a39
 */
Packit 6c0a39
Packit 6c0a39
static int hash_hostname(const char *name,
Packit 6c0a39
                         unsigned char *salt,
Packit 6c0a39
                         unsigned int salt_size,
Packit 6c0a39
                         unsigned char **hash,
Packit 6c0a39
                         unsigned int *hash_size)
Packit 6c0a39
{
Packit 6c0a39
    HMACCTX mac_ctx;
Packit 6c0a39
Packit 6c0a39
    mac_ctx = hmac_init(salt, salt_size, SSH_HMAC_SHA1);
Packit 6c0a39
    if (mac_ctx == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    hmac_update(mac_ctx, name, strlen(name));
Packit 6c0a39
    hmac_final(mac_ctx, *hash, hash_size);
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int match_hashed_hostname(const char *host, const char *hashed_host)
Packit 6c0a39
{
Packit 6c0a39
    char *hashed;
Packit 6c0a39
    char *b64_hash;
Packit 6c0a39
    ssh_buffer salt = NULL;
Packit 6c0a39
    ssh_buffer hash = NULL;
Packit 6c0a39
    unsigned char hashed_buf[256] = {0};
Packit 6c0a39
    unsigned char *hashed_buf_ptr = hashed_buf;
Packit 6c0a39
    unsigned int hashed_buf_size = sizeof(hashed_buf);
Packit 6c0a39
    int cmp;
Packit 6c0a39
    int rc;
Packit 6c0a39
    int match = 0;
Packit 6c0a39
Packit 6c0a39
    cmp = strncmp(hashed_host, "|1|", 3);
Packit 6c0a39
    if (cmp != 0) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    hashed = strdup(hashed_host + 3);
Packit 6c0a39
    if (hashed == NULL) {
Packit 6c0a39
        return 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    b64_hash = strchr(hashed, '|');
Packit 6c0a39
    if (b64_hash == NULL) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
    *b64_hash = '\0';
Packit 6c0a39
    b64_hash++;
Packit 6c0a39
Packit 6c0a39
    salt = base64_to_bin(hashed);
Packit 6c0a39
    if (salt == NULL) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    hash = base64_to_bin(b64_hash);
Packit 6c0a39
    if (hash == NULL) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = hash_hostname(host,
Packit 6c0a39
                       ssh_buffer_get(salt),
Packit 6c0a39
                       ssh_buffer_get_len(salt),
Packit 6c0a39
                       &hashed_buf_ptr,
Packit 6c0a39
                       &hashed_buf_size);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (hashed_buf_size != ssh_buffer_get_len(hash)) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    cmp = memcmp(hashed_buf, ssh_buffer_get(hash), hashed_buf_size);
Packit 6c0a39
    if (cmp == 0) {
Packit 6c0a39
        match = 1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
error:
Packit 6c0a39
    free(hashed);
Packit 6c0a39
    ssh_buffer_free(salt);
Packit 6c0a39
    ssh_buffer_free(hash);
Packit 6c0a39
Packit 6c0a39
    return match;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Free an allocated ssh_knownhosts_entry.
Packit 6c0a39
 *
Packit 6c0a39
 * Use SSH_KNOWNHOSTS_ENTRY_FREE() to set the pointer to NULL.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  entry     The entry to free.
Packit 6c0a39
 */
Packit 6c0a39
void ssh_knownhosts_entry_free(struct ssh_knownhosts_entry *entry)
Packit 6c0a39
{
Packit 6c0a39
    if (entry == NULL) {
Packit 6c0a39
        return;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    SAFE_FREE(entry->hostname);
Packit 6c0a39
    SAFE_FREE(entry->unparsed);
Packit 6c0a39
    ssh_key_free(entry->publickey);
Packit 6c0a39
    SAFE_FREE(entry->comment);
Packit 6c0a39
    SAFE_FREE(entry);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int known_hosts_read_line(FILE *fp,
Packit 6c0a39
                                 char *buf,
Packit 6c0a39
                                 size_t buf_size,
Packit 6c0a39
                                 size_t *buf_len,
Packit 6c0a39
                                 size_t *lineno)
Packit 6c0a39
{
Packit 6c0a39
    while (fgets(buf, buf_size, fp) != NULL) {
Packit 6c0a39
        size_t len;
Packit 6c0a39
        if (buf[0] == '\0') {
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        *lineno += 1;
Packit 6c0a39
        len = strlen(buf);
Packit 6c0a39
        if (buf_len != NULL) {
Packit 6c0a39
            *buf_len = len;
Packit 6c0a39
        }
Packit 6c0a39
        if (buf[len - 1] == '\n' || feof(fp)) {
Packit 6c0a39
            return 0;
Packit 6c0a39
        } else {
Packit 6c0a39
            errno = E2BIG;
Packit 6c0a39
            return -1;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return -1;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static int
Packit 6c0a39
ssh_known_hosts_entries_compare(struct ssh_knownhosts_entry *k1,
Packit 6c0a39
                                struct ssh_knownhosts_entry *k2)
Packit 6c0a39
{
Packit 6c0a39
    int cmp;
Packit 6c0a39
Packit 6c0a39
    if (k1 == NULL || k2 == NULL) {
Packit 6c0a39
        return 1;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    cmp = strcmp(k1->hostname, k2->hostname);
Packit 6c0a39
    if (cmp != 0) {
Packit 6c0a39
        return cmp;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    cmp = ssh_key_cmp(k1->publickey, k2->publickey, SSH_KEY_CMP_PUBLIC);
Packit 6c0a39
    if (cmp != 0) {
Packit 6c0a39
        return cmp;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return 0;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/* This method reads the known_hosts file referenced by the path
Packit 6c0a39
 * in  filename  argument, and entries matching the  match  argument
Packit 6c0a39
 * will be added to the list in  entries  argument.
Packit 6c0a39
 * If the  entries  list is NULL, it will allocate a new list. Caller
Packit 6c0a39
 * is responsible to free it even if an error occurs.
Packit 6c0a39
 */
Packit 6c0a39
static int ssh_known_hosts_read_entries(const char *match,
Packit 6c0a39
                                        const char *filename,
Packit 6c0a39
                                        struct ssh_list **entries)
Packit 6c0a39
{
Packit 6c0a39
    char line[8192];
Packit 6c0a39
    size_t lineno = 0;
Packit 6c0a39
    size_t len = 0;
Packit 6c0a39
    FILE *fp;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    fp = fopen(filename, "r");
Packit 6c0a39
    if (fp == NULL) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN, "Failed to open the known_hosts file '%s': %s",
Packit 6c0a39
                filename, strerror(errno));
Packit 6c0a39
        /* The missing file is not an error here */
Packit 6c0a39
        return SSH_OK;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (*entries == NULL) {
Packit 6c0a39
        *entries = ssh_list_new();
Packit 6c0a39
        if (*entries == NULL) {
Packit 6c0a39
            fclose(fp);
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (rc = known_hosts_read_line(fp, line, sizeof(line), &len, &lineno);
Packit 6c0a39
         rc == 0;
Packit 6c0a39
         rc = known_hosts_read_line(fp, line, sizeof(line), &len, &lineno)) {
Packit 6c0a39
        struct ssh_knownhosts_entry *entry = NULL;
Packit 6c0a39
        struct ssh_iterator *it = NULL;
Packit 6c0a39
        char *p = NULL;
Packit 6c0a39
Packit 6c0a39
        if (line[len] != '\n') {
Packit 6c0a39
            len = strcspn(line, "\n");
Packit 6c0a39
        }
Packit 6c0a39
        line[len] = '\0';
Packit 6c0a39
Packit 6c0a39
        /* Skip leading spaces */
Packit 6c0a39
        for (p = line; isspace((int)p[0]); p++);
Packit 6c0a39
Packit 6c0a39
        /* Skip comments and empty lines */
Packit 6c0a39
        if (p[0] == '\0' || p[0] == '#') {
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* Skip lines starting with markers (@cert-authority, @revoked):
Packit 6c0a39
         * we do not completely support them anyway */
Packit 6c0a39
        if (p[0] == '@') {
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        rc = ssh_known_hosts_parse_line(match,
Packit 6c0a39
                                        line,
Packit 6c0a39
                                        &entry);
Packit 6c0a39
        if (rc == SSH_AGAIN) {
Packit 6c0a39
            continue;
Packit 6c0a39
        } else if (rc != SSH_OK) {
Packit 6c0a39
            goto error;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* Check for duplicates */
Packit 6c0a39
        for (it = ssh_list_get_iterator(*entries);
Packit 6c0a39
             it != NULL;
Packit 6c0a39
             it = it->next) {
Packit 6c0a39
            struct ssh_knownhosts_entry *entry2;
Packit 6c0a39
            int cmp;
Packit 6c0a39
            entry2 = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
Packit 6c0a39
            cmp = ssh_known_hosts_entries_compare(entry, entry2);
Packit 6c0a39
            if (cmp == 0) {
Packit 6c0a39
                ssh_knownhosts_entry_free(entry);
Packit 6c0a39
                entry = NULL;
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        if (entry != NULL) {
Packit 6c0a39
            ssh_list_append(*entries, entry);
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    fclose(fp);
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
error:
Packit 6c0a39
    fclose(fp);
Packit 6c0a39
    return SSH_ERROR;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static char *ssh_session_get_host_port(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    char *host_port;
Packit 6c0a39
    char *host;
Packit 6c0a39
Packit 6c0a39
    if (session->opts.host == NULL) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "Can't verify server in known hosts if the host we "
Packit 6c0a39
                      "should connect to has not been set");
Packit 6c0a39
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    host = ssh_lowercase(session->opts.host);
Packit 6c0a39
    if (host == NULL) {
Packit 6c0a39
        ssh_set_error_oom(session);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->opts.port == 0 || session->opts.port == 22) {
Packit 6c0a39
        host_port = host;
Packit 6c0a39
    } else {
Packit 6c0a39
        host_port = ssh_hostport(host, session->opts.port);
Packit 6c0a39
        SAFE_FREE(host);
Packit 6c0a39
        if (host_port == NULL) {
Packit 6c0a39
            ssh_set_error_oom(session);
Packit 6c0a39
            return NULL;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return host_port;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @internal
Packit 6c0a39
 * @brief Check which host keys should be preferred for the session.
Packit 6c0a39
 *
Packit 6c0a39
 * This checks the known_hosts file to find out which algorithms should be
Packit 6c0a39
 * preferred for the connection we are going to establish.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The ssh session to use.
Packit 6c0a39
 *
Packit 6c0a39
 * @return A list of supported key types, NULL on error.
Packit 6c0a39
 */
Packit 6c0a39
struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    struct ssh_list *entry_list = NULL;
Packit 6c0a39
    struct ssh_iterator *it = NULL;
Packit 6c0a39
    char *host_port = NULL;
Packit 6c0a39
    size_t count;
Packit 6c0a39
    struct ssh_list *list = NULL;
Packit 6c0a39
    int list_error = 0;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (session->opts.knownhosts == NULL ||
Packit 6c0a39
        session->opts.global_knownhosts == NULL) {
Packit 6c0a39
        if (ssh_options_apply(session) < 0) {
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_REQUEST_DENIED,
Packit 6c0a39
                          "Can't find a known_hosts file");
Packit 6c0a39
Packit 6c0a39
            return NULL;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    host_port = ssh_session_get_host_port(session);
Packit 6c0a39
    if (host_port == NULL) {
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    list = ssh_list_new();
Packit 6c0a39
    if (list == NULL) {
Packit 6c0a39
        SAFE_FREE(host_port);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_known_hosts_read_entries(host_port,
Packit 6c0a39
                                      session->opts.knownhosts,
Packit 6c0a39
                                      &entry_list);
Packit 6c0a39
    if (rc != 0) {
Packit 6c0a39
        ssh_list_free(entry_list);
Packit 6c0a39
        ssh_list_free(list);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_known_hosts_read_entries(host_port,
Packit 6c0a39
                                      session->opts.global_knownhosts,
Packit 6c0a39
                                      &entry_list);
Packit 6c0a39
    SAFE_FREE(host_port);
Packit 6c0a39
    if (rc != 0) {
Packit 6c0a39
        ssh_list_free(entry_list);
Packit 6c0a39
        ssh_list_free(list);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (entry_list == NULL) {
Packit 6c0a39
        ssh_list_free(list);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    count = ssh_list_count(entry_list);
Packit 6c0a39
    if (count == 0) {
Packit 6c0a39
        ssh_list_free(list);
Packit 6c0a39
        ssh_list_free(entry_list);
Packit 6c0a39
        return NULL;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (it = ssh_list_get_iterator(entry_list);
Packit 6c0a39
         it != NULL;
Packit 6c0a39
         it = ssh_list_get_iterator(entry_list)) {
Packit 6c0a39
        struct ssh_iterator *it2 = NULL;
Packit 6c0a39
        struct ssh_knownhosts_entry *entry = NULL;
Packit 6c0a39
        const char *algo = NULL;
Packit 6c0a39
        bool present = false;
Packit 6c0a39
Packit 6c0a39
        entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
Packit 6c0a39
        algo = entry->publickey->type_c;
Packit 6c0a39
Packit 6c0a39
        /* Check for duplicates */
Packit 6c0a39
        for (it2 = ssh_list_get_iterator(list);
Packit 6c0a39
             it2 != NULL;
Packit 6c0a39
             it2 = it2->next) {
Packit 6c0a39
            char *alg2 = ssh_iterator_value(char *, it2);
Packit 6c0a39
            int cmp = strcmp(alg2, algo);
Packit 6c0a39
            if (cmp == 0) {
Packit 6c0a39
                present = true;
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        /* Add to the new list only if it is unique */
Packit 6c0a39
        if (!present) {
Packit 6c0a39
            rc = ssh_list_append(list, algo);
Packit 6c0a39
            if (rc != SSH_OK) {
Packit 6c0a39
               list_error = 1;
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        ssh_knownhosts_entry_free(entry);
Packit 6c0a39
        ssh_list_remove(entry_list, it);
Packit 6c0a39
    }
Packit 6c0a39
    ssh_list_free(entry_list);
Packit 6c0a39
    if (list_error) {
Packit 6c0a39
        goto error;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return list;
Packit 6c0a39
error:
Packit 6c0a39
    ssh_list_free(list);
Packit 6c0a39
    return NULL;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Parse a line from a known_hosts entry into a structure
Packit 6c0a39
 *
Packit 6c0a39
 * This parses an known_hosts entry into a structure with the key in a libssh
Packit 6c0a39
 * consumeable form. You can use the PKI key function to further work with it.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  hostname     The hostname to match the line to
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  line         The line to compare and parse if we have a hostname
Packit 6c0a39
 *                          match.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  entry        A pointer to store the the allocated known_hosts
Packit 6c0a39
 *                          entry structure. The user needs to free the memory
Packit 6c0a39
 *                          using SSH_KNOWNHOSTS_ENTRY_FREE().
Packit 6c0a39
 *
Packit 6c0a39
 * @return SSH_OK on success, SSH_ERROR otherwise.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_known_hosts_parse_line(const char *hostname,
Packit 6c0a39
                               const char *line,
Packit 6c0a39
                               struct ssh_knownhosts_entry **entry)
Packit 6c0a39
{
Packit 6c0a39
    struct ssh_knownhosts_entry *e = NULL;
Packit 6c0a39
    char *known_host = NULL;
Packit 6c0a39
    char *p;
Packit 6c0a39
    enum ssh_keytypes_e key_type;
Packit 6c0a39
    int match = 0;
Packit 6c0a39
    int rc = SSH_OK;
Packit 6c0a39
Packit 6c0a39
    known_host = strdup(line);
Packit 6c0a39
    if (known_host == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* match pattern for hostname or hashed hostname */
Packit 6c0a39
    p = strtok(known_host, " ");
Packit 6c0a39
    if (p == NULL ) {
Packit 6c0a39
        free(known_host);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    e = calloc(1, sizeof(struct ssh_knownhosts_entry));
Packit 6c0a39
    if (e == NULL) {
Packit 6c0a39
        free(known_host);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (hostname != NULL) {
Packit 6c0a39
        char *host_port = NULL;
Packit 6c0a39
        char *q = NULL;
Packit 6c0a39
Packit 6c0a39
        /* Hashed */
Packit 6c0a39
        if (p[0] == '|') {
Packit 6c0a39
            match = match_hashed_hostname(hostname, p);
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        for (q = strtok(p, ",");
Packit 6c0a39
             q != NULL;
Packit 6c0a39
             q = strtok(NULL, ",")) {
Packit 6c0a39
            int cmp;
Packit 6c0a39
Packit 6c0a39
            if (q[0] == '[' && hostname[0] != '[') {
Packit 6c0a39
                /* Corner case: We have standard port so we do not have
Packit 6c0a39
                 * hostname in square braces. But the patern is enclosed
Packit 6c0a39
                 * in braces with, possibly standard or wildcard, port.
Packit 6c0a39
                 * We need to test against [host]:port pair here.
Packit 6c0a39
                 */
Packit 6c0a39
                if (host_port == NULL) {
Packit 6c0a39
                    host_port = ssh_hostport(hostname, 22);
Packit 6c0a39
                    if (host_port == NULL) {
Packit 6c0a39
                        rc = SSH_ERROR;
Packit 6c0a39
                        goto out;
Packit 6c0a39
                    }
Packit 6c0a39
                }
Packit 6c0a39
Packit 6c0a39
                cmp = match_hostname(host_port, q, strlen(q));
Packit 6c0a39
            } else {
Packit 6c0a39
                cmp = match_hostname(hostname, q, strlen(q));
Packit 6c0a39
            }
Packit 6c0a39
            if (cmp == 1) {
Packit 6c0a39
                match = 1;
Packit 6c0a39
                break;
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
        free(host_port);
Packit 6c0a39
Packit 6c0a39
        if (match == 0) {
Packit 6c0a39
            rc = SSH_AGAIN;
Packit 6c0a39
            goto out;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        e->hostname = strdup(hostname);
Packit 6c0a39
        if (e->hostname == NULL) {
Packit 6c0a39
            rc = SSH_ERROR;
Packit 6c0a39
            goto out;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Restart parsing */
Packit 6c0a39
    SAFE_FREE(known_host);
Packit 6c0a39
    known_host = strdup(line);
Packit 6c0a39
    if (known_host == NULL) {
Packit 6c0a39
        rc = SSH_ERROR;
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    p = strtok(known_host, " ");
Packit 6c0a39
    if (p == NULL ) {
Packit 6c0a39
        rc = SSH_ERROR;
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    e->unparsed = strdup(p);
Packit 6c0a39
    if (e->unparsed == NULL) {
Packit 6c0a39
        rc = SSH_ERROR;
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* pubkey type */
Packit 6c0a39
    p = strtok(NULL, " ");
Packit 6c0a39
    if (p == NULL) {
Packit 6c0a39
        rc = SSH_ERROR;
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    key_type = ssh_key_type_from_name(p);
Packit 6c0a39
    if (key_type == SSH_KEYTYPE_UNKNOWN) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN, "key type '%s' unknown!", p);
Packit 6c0a39
        rc = SSH_ERROR;
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* public key */
Packit 6c0a39
    p = strtok(NULL, " ");
Packit 6c0a39
    if (p == NULL) {
Packit 6c0a39
        rc = SSH_ERROR;
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_pki_import_pubkey_base64(p,
Packit 6c0a39
                                      key_type,
Packit 6c0a39
                                      &e->publickey);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        SSH_LOG(SSH_LOG_WARN,
Packit 6c0a39
                "Failed to parse %s key for entry: %s!",
Packit 6c0a39
                ssh_key_type_to_char(key_type),
Packit 6c0a39
                e->unparsed);
Packit 6c0a39
        goto out;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* comment */
Packit 6c0a39
    p = strtok(NULL, " ");
Packit 6c0a39
    if (p != NULL) {
Packit 6c0a39
        p = strstr(line, p);
Packit 6c0a39
        if (p != NULL) {
Packit 6c0a39
            e->comment = strdup(p);
Packit 6c0a39
            if (e->comment == NULL) {
Packit 6c0a39
                rc = SSH_ERROR;
Packit 6c0a39
                goto out;
Packit 6c0a39
            }
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    *entry = e;
Packit 6c0a39
    SAFE_FREE(known_host);
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
out:
Packit 6c0a39
    SAFE_FREE(known_host);
Packit 6c0a39
    ssh_knownhosts_entry_free(e);
Packit 6c0a39
    return rc;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Check if the set hostname and port matches an entry in known_hosts.
Packit 6c0a39
 *
Packit 6c0a39
 * This check if the set hostname and port has an entry in the known_hosts file.
Packit 6c0a39
 * You need to set at least the hostname using ssh_options_set().
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The session with with the values set to check.
Packit 6c0a39
 *
Packit 6c0a39
 * @return A ssh_known_hosts_e return value.
Packit 6c0a39
 */
Packit 6c0a39
enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    struct ssh_list *entry_list = NULL;
Packit 6c0a39
    struct ssh_iterator *it = NULL;
Packit 6c0a39
    char *host_port = NULL;
Packit 6c0a39
    bool global_known_hosts_found = false;
Packit 6c0a39
    bool known_hosts_found = false;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (session->opts.knownhosts == NULL) {
Packit 6c0a39
        if (ssh_options_apply(session) < 0) {
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_REQUEST_DENIED,
Packit 6c0a39
                          "Cannot find a known_hosts file");
Packit 6c0a39
Packit 6c0a39
            return SSH_KNOWN_HOSTS_NOT_FOUND;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->opts.knownhosts == NULL &&
Packit 6c0a39
        session->opts.global_knownhosts == NULL) {
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_REQUEST_DENIED,
Packit 6c0a39
                          "No path set for a known_hosts file");
Packit 6c0a39
Packit 6c0a39
            return SSH_KNOWN_HOSTS_NOT_FOUND;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->opts.knownhosts != NULL) {
Packit 6c0a39
        known_hosts_found = ssh_file_readaccess_ok(session->opts.knownhosts);
Packit 6c0a39
        if (!known_hosts_found) {
Packit 6c0a39
            SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
Packit 6c0a39
                    session->opts.knownhosts);
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->opts.global_knownhosts != NULL) {
Packit 6c0a39
        global_known_hosts_found =
Packit 6c0a39
                ssh_file_readaccess_ok(session->opts.global_knownhosts);
Packit 6c0a39
        if (!global_known_hosts_found) {
Packit 6c0a39
            SSH_LOG(SSH_LOG_WARN, "Cannot access file %s",
Packit 6c0a39
                    session->opts.global_knownhosts);
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if ((!known_hosts_found) && (!global_known_hosts_found)) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_REQUEST_DENIED,
Packit 6c0a39
                      "Cannot find a known_hosts file");
Packit 6c0a39
Packit 6c0a39
        return SSH_KNOWN_HOSTS_NOT_FOUND;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    host_port = ssh_session_get_host_port(session);
Packit 6c0a39
    if (host_port == NULL) {
Packit 6c0a39
        return SSH_KNOWN_HOSTS_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit Service c3151f
    if (session->opts.knownhosts != NULL) {
Packit 6c0a39
        rc = ssh_known_hosts_read_entries(host_port,
Packit 6c0a39
                                          session->opts.knownhosts,
Packit 6c0a39
                                          &entry_list);
Packit 6c0a39
        if (rc != 0) {
Packit 6c0a39
            SAFE_FREE(host_port);
Packit 6c0a39
            ssh_list_free(entry_list);
Packit 6c0a39
            return SSH_KNOWN_HOSTS_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit Service c3151f
    if (session->opts.global_knownhosts != NULL) {
Packit 6c0a39
        rc = ssh_known_hosts_read_entries(host_port,
Packit 6c0a39
                                          session->opts.global_knownhosts,
Packit 6c0a39
                                          &entry_list);
Packit Service c3151f
        SAFE_FREE(host_port);
Packit 6c0a39
        if (rc != 0) {
Packit 6c0a39
            ssh_list_free(entry_list);
Packit 6c0a39
            return SSH_KNOWN_HOSTS_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (ssh_list_count(entry_list) == 0) {
Packit 6c0a39
        ssh_list_free(entry_list);
Packit 6c0a39
        return SSH_KNOWN_HOSTS_UNKNOWN;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (it = ssh_list_get_iterator(entry_list);
Packit 6c0a39
         it != NULL;
Packit 6c0a39
         it = ssh_list_get_iterator(entry_list)) {
Packit 6c0a39
        struct ssh_knownhosts_entry *entry = NULL;
Packit 6c0a39
Packit 6c0a39
        entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
Packit 6c0a39
        ssh_knownhosts_entry_free(entry);
Packit 6c0a39
        ssh_list_remove(entry_list, it);
Packit 6c0a39
    }
Packit 6c0a39
    ssh_list_free(entry_list);
Packit 6c0a39
Packit 6c0a39
    return SSH_KNOWN_HOSTS_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Export the current session information to a known_hosts string.
Packit 6c0a39
 *
Packit 6c0a39
 * This exports the current information of a session which is connected so a
Packit 6c0a39
 * ssh server into an entry line which can be added to a known_hosts file.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The session with information to export.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  pentry_string A pointer to a string to store the alloocated
Packit 6c0a39
 *                           line of the entry. The user must free it using
Packit 6c0a39
 *                           ssh_string_free_char().
Packit 6c0a39
 *
Packit 6c0a39
 * @return SSH_OK on succcess, SSH_ERROR otherwise.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_session_export_known_hosts_entry(ssh_session session,
Packit 6c0a39
                                         char **pentry_string)
Packit 6c0a39
{
Packit 6c0a39
    ssh_key server_pubkey = NULL;
Packit 6c0a39
    char *host = NULL;
Packit 6c0a39
    char entry_buf[4096] = {0};
Packit 6c0a39
    char *b64_key = NULL;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (pentry_string == NULL) {
Packit 6c0a39
        ssh_set_error_invalid(session);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->opts.host == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                      "Can't create known_hosts entry - hostname unknown");
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    host = ssh_session_get_host_port(session);
Packit 6c0a39
    if (host == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (session->current_crypto == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                      "No current crypto context, please connect first");
Packit 6c0a39
        SAFE_FREE(host);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    server_pubkey = ssh_dh_get_current_server_publickey(session);
Packit 6c0a39
    if (server_pubkey == NULL){
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "No public key present");
Packit 6c0a39
        SAFE_FREE(host);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_pki_export_pubkey_base64(server_pubkey, &b64_key);
Packit 6c0a39
    if (rc < 0) {
Packit 6c0a39
        SAFE_FREE(host);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    snprintf(entry_buf, sizeof(entry_buf),
Packit 6c0a39
                "%s %s %s\n",
Packit 6c0a39
                host,
Packit 6c0a39
                server_pubkey->type_c,
Packit 6c0a39
                b64_key);
Packit 6c0a39
Packit 6c0a39
    SAFE_FREE(host);
Packit 6c0a39
    SAFE_FREE(b64_key);
Packit 6c0a39
Packit 6c0a39
    *pentry_string = strdup(entry_buf);
Packit 6c0a39
    if (*pentry_string == NULL) {
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Add the current connected server to the user known_hosts file.
Packit 6c0a39
 *
Packit 6c0a39
 * This adds the currently connected server to the known_hosts file by
Packit 6c0a39
 * appending a new line at the end. The global known_hosts file is considered
Packit 6c0a39
 * read-only so it is not touched by this function.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The session to use to write the entry.
Packit 6c0a39
 *
Packit 6c0a39
 * @return SSH_OK on success, SSH_ERROR otherwise.
Packit 6c0a39
 */
Packit 6c0a39
int ssh_session_update_known_hosts(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    FILE *fp = NULL;
Packit 6c0a39
    char *entry = NULL;
Packit 6c0a39
    char *dir = NULL;
Packit 6c0a39
    size_t nwritten;
Packit 6c0a39
    size_t len;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    if (session->opts.knownhosts == NULL) {
Packit 6c0a39
        rc = ssh_options_apply(session);
Packit 6c0a39
        if (rc != SSH_OK) {
Packit 6c0a39
            ssh_set_error(session, SSH_FATAL, "Can't find a known_hosts file");
Packit 6c0a39
            return SSH_ERROR;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* Check if directory exists and create it if not */
Packit 6c0a39
    dir = ssh_dirname(session->opts.knownhosts);
Packit 6c0a39
    if (dir == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL, "%s", strerror(errno));
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_file_readaccess_ok(dir);
Packit 6c0a39
    if (rc == 0) {
Packit 6c0a39
        rc = ssh_mkdir(dir, 0700);
Packit 6c0a39
    } else {
Packit 6c0a39
        rc = 0;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    if (rc != 0) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                      "Cannot create %s directory.", dir);
Packit 6c0a39
        SAFE_FREE(dir);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
    SAFE_FREE(dir);
Packit 6c0a39
Packit 6c0a39
    fp = fopen(session->opts.knownhosts, "a");
Packit 6c0a39
    if (fp == NULL) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                "Couldn't open known_hosts file %s for appending: %s",
Packit 6c0a39
                session->opts.knownhosts, strerror(errno));
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rc = ssh_session_export_known_hosts_entry(session, &entry);
Packit 6c0a39
    if (rc != SSH_OK) {
Packit 6c0a39
        fclose(fp);
Packit 6c0a39
        return rc;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    len = strlen(entry);
Packit 6c0a39
    nwritten = fwrite(entry, sizeof(char), len, fp);
Packit 6c0a39
    SAFE_FREE(entry);
Packit 6c0a39
    if (nwritten != len || ferror(fp)) {
Packit 6c0a39
        ssh_set_error(session, SSH_FATAL,
Packit 6c0a39
                      "Couldn't append to known_hosts file %s: %s",
Packit 6c0a39
                      session->opts.knownhosts, strerror(errno));
Packit 6c0a39
        fclose(fp);
Packit 6c0a39
        return SSH_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    fclose(fp);
Packit 6c0a39
    return SSH_OK;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
static enum ssh_known_hosts_e
Packit 6c0a39
ssh_known_hosts_check_server_key(const char *hosts_entry,
Packit 6c0a39
                                 const char *filename,
Packit 6c0a39
                                 ssh_key server_key,
Packit 6c0a39
                                 struct ssh_knownhosts_entry **pentry)
Packit 6c0a39
{
Packit 6c0a39
    struct ssh_list *entry_list = NULL;
Packit 6c0a39
    struct ssh_iterator *it = NULL;
Packit 6c0a39
    enum ssh_known_hosts_e found = SSH_KNOWN_HOSTS_UNKNOWN;
Packit 6c0a39
    int rc;
Packit 6c0a39
Packit 6c0a39
    rc = ssh_known_hosts_read_entries(hosts_entry,
Packit 6c0a39
                                      filename,
Packit 6c0a39
                                      &entry_list);
Packit 6c0a39
    if (rc != 0) {
Packit 6c0a39
        ssh_list_free(entry_list);
Packit 6c0a39
        return SSH_KNOWN_HOSTS_UNKNOWN;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    it = ssh_list_get_iterator(entry_list);
Packit 6c0a39
    if (it == NULL) {
Packit 6c0a39
        ssh_list_free(entry_list);
Packit 6c0a39
        return SSH_KNOWN_HOSTS_UNKNOWN;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (;it != NULL; it = it->next) {
Packit 6c0a39
        struct ssh_knownhosts_entry *entry = NULL;
Packit 6c0a39
        int cmp;
Packit 6c0a39
Packit 6c0a39
        entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
Packit 6c0a39
Packit 6c0a39
        cmp = ssh_key_cmp(server_key, entry->publickey, SSH_KEY_CMP_PUBLIC);
Packit 6c0a39
        if (cmp == 0) {
Packit 6c0a39
            found = SSH_KNOWN_HOSTS_OK;
Packit 6c0a39
            if (pentry != NULL) {
Packit 6c0a39
                *pentry = entry;
Packit 6c0a39
                ssh_list_remove(entry_list, it);
Packit 6c0a39
            }
Packit 6c0a39
            break;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        if (ssh_key_type(server_key) == ssh_key_type(entry->publickey)) {
Packit 6c0a39
            found = SSH_KNOWN_HOSTS_CHANGED;
Packit 6c0a39
            continue;
Packit 6c0a39
        }
Packit 6c0a39
Packit 6c0a39
        if (found != SSH_KNOWN_HOSTS_CHANGED) {
Packit 6c0a39
            found = SSH_KNOWN_HOSTS_OTHER;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    for (it = ssh_list_get_iterator(entry_list);
Packit 6c0a39
         it != NULL;
Packit 6c0a39
         it = ssh_list_get_iterator(entry_list)) {
Packit 6c0a39
        struct ssh_knownhosts_entry *entry = NULL;
Packit 6c0a39
Packit 6c0a39
        entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
Packit 6c0a39
        ssh_knownhosts_entry_free(entry);
Packit 6c0a39
        ssh_list_remove(entry_list, it);
Packit 6c0a39
    }
Packit 6c0a39
    ssh_list_free(entry_list);
Packit 6c0a39
Packit 6c0a39
    return found;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the known_hosts entry for the current connected session.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The session to validate.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  pentry   A pointer to store the allocated known hosts entry.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_KNOWN_HOSTS_OK:        The server is known and has not changed.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_CHANGED:   The server key has changed. Either you
Packit 6c0a39
 *                                     are under attack or the administrator
Packit 6c0a39
 *                                     changed the key. You HAVE to warn the
Packit 6c0a39
 *                                     user about a possible attack.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_OTHER:     The server gave use a key of a type while
Packit 6c0a39
 *                                     we had an other type recorded. It is a
Packit 6c0a39
 *                                     possible attack.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_UNKNOWN:   The server is unknown. User should
Packit 6c0a39
 *                                     confirm the public key hash is correct.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
Packit 6c0a39
 *                                     host is thus unknown. File will be
Packit 6c0a39
 *                                     created if host key is accepted.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_ERROR:     There had been an eror checking the host.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_knownhosts_entry_free()
Packit 6c0a39
 */
Packit 6c0a39
enum ssh_known_hosts_e
Packit 6c0a39
ssh_session_get_known_hosts_entry(ssh_session session,
Packit 6c0a39
                                  struct ssh_knownhosts_entry **pentry)
Packit 6c0a39
{
Packit 6c0a39
    enum ssh_known_hosts_e old_rv, rv = SSH_KNOWN_HOSTS_UNKNOWN;
Packit 6c0a39
Packit 6c0a39
    if (session->opts.knownhosts == NULL) {
Packit 6c0a39
        if (ssh_options_apply(session) < 0) {
Packit 6c0a39
            ssh_set_error(session,
Packit 6c0a39
                          SSH_REQUEST_DENIED,
Packit 6c0a39
                          "Can't find a known_hosts file");
Packit 6c0a39
Packit 6c0a39
            return SSH_KNOWN_HOSTS_NOT_FOUND;
Packit 6c0a39
        }
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    rv = ssh_session_get_known_hosts_entry_file(session,
Packit 6c0a39
                                                session->opts.knownhosts,
Packit 6c0a39
                                                pentry);
Packit 6c0a39
    if (rv == SSH_KNOWN_HOSTS_OK) {
Packit 6c0a39
        /* We already found a match in the first file: return */
Packit 6c0a39
        return rv;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    old_rv = rv;
Packit 6c0a39
    rv = ssh_session_get_known_hosts_entry_file(session,
Packit 6c0a39
                                                session->opts.global_knownhosts,
Packit 6c0a39
                                                pentry);
Packit 6c0a39
Packit 6c0a39
    /* If we did not find any match at all:  we report the previous result */
Packit 6c0a39
    if (rv == SSH_KNOWN_HOSTS_UNKNOWN) {
Packit 6c0a39
        if (session->opts.StrictHostKeyChecking == 0) {
Packit 6c0a39
            return SSH_KNOWN_HOSTS_OK;
Packit 6c0a39
        }
Packit 6c0a39
        return old_rv;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    /* We found some match: return it */
Packit 6c0a39
    return rv;
Packit 6c0a39
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Get the known_hosts entry for the current connected session
Packit 6c0a39
 *        from the given known_hosts file.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The session to validate.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  filename The filename to parse.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  pentry   A pointer to store the allocated known hosts entry.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_KNOWN_HOSTS_OK:        The server is known and has not changed.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_CHANGED:   The server key has changed. Either you
Packit 6c0a39
 *                                     are under attack or the administrator
Packit 6c0a39
 *                                     changed the key. You HAVE to warn the
Packit 6c0a39
 *                                     user about a possible attack.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_OTHER:     The server gave use a key of a type while
Packit 6c0a39
 *                                     we had an other type recorded. It is a
Packit 6c0a39
 *                                     possible attack.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_UNKNOWN:   The server is unknown. User should
Packit 6c0a39
 *                                     confirm the public key hash is correct.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
Packit 6c0a39
 *                                     host is thus unknown. File will be
Packit 6c0a39
 *                                     created if host key is accepted.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_ERROR:     There had been an eror checking the host.
Packit 6c0a39
 *
Packit 6c0a39
 * @see ssh_knownhosts_entry_free()
Packit 6c0a39
 */
Packit 6c0a39
enum ssh_known_hosts_e
Packit 6c0a39
ssh_session_get_known_hosts_entry_file(ssh_session session,
Packit 6c0a39
                                       const char *filename,
Packit 6c0a39
                                       struct ssh_knownhosts_entry **pentry)
Packit 6c0a39
{
Packit 6c0a39
    ssh_key server_pubkey = NULL;
Packit 6c0a39
    char *host_port = NULL;
Packit 6c0a39
    enum ssh_known_hosts_e found = SSH_KNOWN_HOSTS_UNKNOWN;
Packit 6c0a39
Packit 6c0a39
    server_pubkey = ssh_dh_get_current_server_publickey(session);
Packit 6c0a39
    if (server_pubkey == NULL) {
Packit 6c0a39
        ssh_set_error(session,
Packit 6c0a39
                      SSH_FATAL,
Packit 6c0a39
                      "ssh_session_is_known_host called without a "
Packit 6c0a39
                      "server_key!");
Packit 6c0a39
Packit 6c0a39
        return SSH_KNOWN_HOSTS_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    host_port = ssh_session_get_host_port(session);
Packit 6c0a39
    if (host_port == NULL) {
Packit 6c0a39
        return SSH_KNOWN_HOSTS_ERROR;
Packit 6c0a39
    }
Packit 6c0a39
Packit 6c0a39
    found = ssh_known_hosts_check_server_key(host_port,
Packit 6c0a39
                                             filename,
Packit 6c0a39
                                             server_pubkey,
Packit 6c0a39
                                             pentry);
Packit 6c0a39
    SAFE_FREE(host_port);
Packit 6c0a39
Packit 6c0a39
    return found;
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/**
Packit 6c0a39
 * @brief Check if the servers public key for the connected session is known.
Packit 6c0a39
 *
Packit 6c0a39
 * This checks if we already know the public key of the server we want to
Packit 6c0a39
 * connect to. This allows to detect if there is a MITM attach going on
Packit 6c0a39
 * of if there have been changes on the server we don't know about.
Packit 6c0a39
 *
Packit 6c0a39
 * @param[in]  session  The SSH to validate.
Packit 6c0a39
 *
Packit 6c0a39
 * @returns SSH_KNOWN_HOSTS_OK:        The server is known and has not changed.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_CHANGED:   The server key has changed. Either you
Packit 6c0a39
 *                                     are under attack or the administrator
Packit 6c0a39
 *                                     changed the key. You HAVE to warn the
Packit 6c0a39
 *                                     user about a possible attack.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_OTHER:     The server gave use a key of a type while
Packit 6c0a39
 *                                     we had an other type recorded. It is a
Packit 6c0a39
 *                                     possible attack.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_UNKNOWN:   The server is unknown. User should
Packit 6c0a39
 *                                     confirm the public key hash is correct.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_NOT_FOUND: The known host file does not exist. The
Packit 6c0a39
 *                                     host is thus unknown. File will be
Packit 6c0a39
 *                                     created if host key is accepted.\n
Packit 6c0a39
 *          SSH_KNOWN_HOSTS_ERROR:     There had been an error checking the host.
Packit 6c0a39
 */
Packit 6c0a39
enum ssh_known_hosts_e ssh_session_is_known_server(ssh_session session)
Packit 6c0a39
{
Packit 6c0a39
    return ssh_session_get_known_hosts_entry(session, NULL);
Packit 6c0a39
}
Packit 6c0a39
Packit 6c0a39
/** @} */