|
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 |
/** @} */
|