knownhost: support [host]:port in knownhost file
OpenSSH has ways to add hosts to the knownhosts file that include a specific port number which makes the key associated with only that specific host+port pair. libssh2 previously did not support this, and I was forced to add a new function to the API to properly expose this ability to applications: libssh2_knownhost_checkp() To *add* such hosts to the knownhosts file, you make sure to pass on the host name in that manner to the libssh2_knownhost_addc() function.
Этот коммит содержится в:
родитель
5184aec461
Коммит
a3f3347c12
3
TODO
3
TODO
@ -55,3 +55,6 @@ At next SONAME bump
|
||||
|
||||
* remove the existing libssh2_scp_send_ex() function and rename
|
||||
libssh2_scp_send64 to become the new libssh2_scp_send instead.
|
||||
|
||||
* remove the existing libssh2_knownhost_check() functin and rename
|
||||
libssh2_knownhost_checkp() to become the new libssh2_knownhost_check instead
|
||||
|
@ -7,12 +7,12 @@ libssh2_knownhost_add - add a known host
|
||||
.SH SYNOPSIS
|
||||
#include <libssh2.h>
|
||||
|
||||
int libssh2_knownhost_add(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
char *host, char *salt,
|
||||
char *key, size_t keylen,
|
||||
const char *comment, size_t commentlen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **store);
|
||||
int libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
char *host, char *salt,
|
||||
char *key, size_t keylen,
|
||||
const char *comment, size_t commentlen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **store);
|
||||
.SH DESCRIPTION
|
||||
Adds a known host to the collection of known hosts identified by the 'hosts'
|
||||
handle.
|
||||
@ -21,7 +21,12 @@ handle.
|
||||
must be provided base64 encoded. The host name can be the IP numerical address
|
||||
of the host or the full name.
|
||||
|
||||
\fIsalt\P is a pointer to the salt used for the host hashing, if the host is
|
||||
If you want to add a key for a specific port number for the given host, you
|
||||
must provide the host name like '[host]:port' with the actual characters '['
|
||||
and ']' enclosing the host name and a colon separating the host part from the
|
||||
port number. For example: \&"[host.example.com]:222".
|
||||
|
||||
\fIsalt\fP is a pointer to the salt used for the host hashing, if the host is
|
||||
provided hashed. If the host is provided in plain text, salt has no meaning.
|
||||
The salt has to be provided base64 encoded with a trailing zero byte.
|
||||
|
||||
|
63
docs/libssh2_knownhost_checkp.3
Обычный файл
63
docs/libssh2_knownhost_checkp.3
Обычный файл
@ -0,0 +1,63 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2009-2010 by Daniel Stenberg
|
||||
.\"
|
||||
.TH libssh2_knownhost_check 3 "1 May 2010" "libssh2 1.2.6" "libssh2 manual"
|
||||
.SH NAME
|
||||
libssh2_knownhost_checkp - check a host+key against the list of known hosts
|
||||
.SH SYNOPSIS
|
||||
#include <libssh2.h>
|
||||
|
||||
int libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
const char *host, int port,
|
||||
const char *key, size_t keylen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **knownhost);
|
||||
.SH DESCRIPTION
|
||||
Checks a host and its associated key against the collection of known hosts,
|
||||
and returns info back about the (partially) matched entry.
|
||||
|
||||
\fIhost\fP is a pointer the host name in plain text. The host name can be the
|
||||
IP numerical address of the host or the full name.
|
||||
|
||||
\fIport\fP is the port number used by the host (or 0 to check the generic
|
||||
host). If the port number is given, libssh2 will check the key for the
|
||||
specific host + port number combination in addition to the plain host name
|
||||
only check.
|
||||
|
||||
\fIkey\fP is a pointer to the key for the given host.
|
||||
|
||||
\fIkeylen\fP is the total size in bytes of the key pointed to by the \fIkey\fP
|
||||
argument
|
||||
|
||||
\fItypemask\fP is a bitmask that specifies format and info about the data
|
||||
passed to this function. Specificly, it details what format the host name is,
|
||||
what format the key is and what key type it is.
|
||||
|
||||
The host name is given as one of the following types:
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN or LIBSSH2_KNOWNHOST_TYPE_CUSTOM.
|
||||
|
||||
The key is encoded using one of the following encodings:
|
||||
LIBSSH2_KNOWNHOST_KEYENC_RAW or LIBSSH2_KNOWNHOST_KEYENC_BASE64.
|
||||
|
||||
\fIknownhost\fP if set to non-NULL, it must be a pointer to a 'struct
|
||||
libssh2_knownhost' pointer that gets filled in to point to info about a known
|
||||
host that matches or partially matches.
|
||||
.SH RETURN VALUE
|
||||
\fIlibssh2_knownhost_check(3)\fP returns info about how well the provided
|
||||
host + key pair matched one of the entries in the list of known hosts.
|
||||
|
||||
LIBSSH2_KNOWNHOST_CHECK_FAILURE - something prevented the check to be made
|
||||
|
||||
LIBSSH2_KNOWNHOST_CHECK_NOTFOUND - no host match was found
|
||||
|
||||
LIBSSH2_KNOWNHOST_CHECK_MATCH - hosts and keys match.
|
||||
|
||||
LIBSSH2_KNOWNHOST_CHECK_MISMATCH - host was found, but the keys didn't match!
|
||||
.SH AVAILABILITY
|
||||
Added in libssh2 1.2.6
|
||||
.SH EXAMPLE
|
||||
See the ssh2_exec.c example as provided in the tarball.
|
||||
.SH SEE ALSO
|
||||
.BR libssh2_knownhost_init(3)
|
||||
.BR libssh2_knownhost_free(3)
|
||||
.BR libssh2_knownhost_add(3)
|
@ -167,12 +167,21 @@ int main(int argc, char *argv[])
|
||||
fingerprint = libssh2_session_hostkey(session, &len, &type);
|
||||
if(fingerprint) {
|
||||
struct libssh2_knownhost *host;
|
||||
int check = libssh2_knownhost_check(nh, (char *)hostname,
|
||||
(char *)fingerprint, len,
|
||||
#if LIBSSH2_VERSION_NUM >= 0x010206
|
||||
/* introduced in 1.2.6 */
|
||||
int check = libssh2_knownhost_checkp(nh, hostname, 22,
|
||||
fingerprint, len,
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
|
||||
LIBSSH2_KNOWNHOST_KEYENC_RAW,
|
||||
&host);
|
||||
#else
|
||||
/* 1.2.5 or older */
|
||||
int check = libssh2_knownhost_check(nh, hostname,
|
||||
fingerprint, len,
|
||||
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
|
||||
LIBSSH2_KNOWNHOST_KEYENC_RAW,
|
||||
&host);
|
||||
|
||||
#endif
|
||||
fprintf(stderr, "Host check: %d, key: %s\n", check,
|
||||
(check <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
|
||||
host->key:"<none>");
|
||||
|
@ -856,6 +856,15 @@ libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **knownhost);
|
||||
|
||||
/* this function is identital to the above one, but also takes a port
|
||||
argument that allows libssh2 to do a better check */
|
||||
LIBSSH2_API int
|
||||
libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
const char *host, int port,
|
||||
const char *key, size_t keylen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **knownhost);
|
||||
|
||||
/*
|
||||
* libssh2_knownhost_del
|
||||
*
|
||||
|
201
src/knownhost.c
201
src/knownhost.c
@ -43,6 +43,8 @@ struct known_host {
|
||||
struct list_node node;
|
||||
char *name; /* points to the name or the hash (allocated) */
|
||||
size_t name_len; /* needed for hashed data */
|
||||
int port; /* if non-zero, a specific port this key is for on this
|
||||
host */
|
||||
int typemask; /* plain, sha1, custom, ... */
|
||||
char *salt; /* points to binary salt (allocated) */
|
||||
size_t salt_len; /* size of salt */
|
||||
@ -309,7 +311,7 @@ libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
}
|
||||
|
||||
/*
|
||||
* libssh2_knownhost_check
|
||||
* knownhost_check
|
||||
*
|
||||
* Check a host and its associated key against the collection of known hosts.
|
||||
*
|
||||
@ -326,17 +328,22 @@ libssh2_knownhost_addc(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
* LIBSSH2_KNOWNHOST_CHECK_MATCH
|
||||
* LIBSSH2_KNOWNHOST_CHECK_MISMATCH
|
||||
*/
|
||||
LIBSSH2_API int
|
||||
libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
const char *host, const char *key, size_t keylen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **ext)
|
||||
static int
|
||||
knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
const char *hostp, int port,
|
||||
const char *key, size_t keylen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **ext)
|
||||
{
|
||||
struct known_host *node = _libssh2_list_first(&hosts->head);
|
||||
struct known_host *node;
|
||||
struct known_host *badkey = NULL;
|
||||
int type = typemask & LIBSSH2_KNOWNHOST_TYPE_MASK;
|
||||
char *keyalloc = NULL;
|
||||
int rc = LIBSSH2_KNOWNHOST_CHECK_NOTFOUND;
|
||||
char hostbuff[270]; /* most host names can't be longer than like 256 */
|
||||
const char *host;
|
||||
int numcheck; /* number of host combos to check */
|
||||
int match = 0;
|
||||
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_SHA1)
|
||||
/* we can't work with a sha1 as given input */
|
||||
@ -357,61 +364,80 @@ libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
key = keyalloc;
|
||||
}
|
||||
|
||||
while (node) {
|
||||
int match = 0;
|
||||
switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) {
|
||||
case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN)
|
||||
match = !strcmp(host, node->name);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM)
|
||||
match = !strcmp(host, node->name);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_TYPE_SHA1:
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) {
|
||||
/* when we have the sha1 version stored, we can use a plain
|
||||
input to produce a hash to compare with the stored hash.
|
||||
*/
|
||||
libssh2_hmac_ctx ctx;
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
/* if a port number is given, check for a '[host]:port' first before the
|
||||
plain 'host' */
|
||||
if(port) {
|
||||
snprintf(hostbuff, sizeof(hostbuff), "[%s]:%d", hostp, port);
|
||||
host = hostbuff;
|
||||
numcheck = 2; /* check both combos, start with this */
|
||||
}
|
||||
else {
|
||||
host = hostp;
|
||||
numcheck = 1; /* only check this host version */
|
||||
}
|
||||
|
||||
if(SHA_DIGEST_LENGTH != node->name_len) {
|
||||
/* the name hash length must be the sha1 size or
|
||||
we can't match it */
|
||||
break;
|
||||
do {
|
||||
node = _libssh2_list_first(&hosts->head);
|
||||
while (node) {
|
||||
switch(node->typemask & LIBSSH2_KNOWNHOST_TYPE_MASK) {
|
||||
case LIBSSH2_KNOWNHOST_TYPE_PLAIN:
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN)
|
||||
match = !strcmp(host, node->name);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_TYPE_CUSTOM:
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_CUSTOM)
|
||||
match = !strcmp(host, node->name);
|
||||
break;
|
||||
case LIBSSH2_KNOWNHOST_TYPE_SHA1:
|
||||
if(type == LIBSSH2_KNOWNHOST_TYPE_PLAIN) {
|
||||
/* when we have the sha1 version stored, we can use a
|
||||
plain input to produce a hash to compare with the
|
||||
stored hash.
|
||||
*/
|
||||
libssh2_hmac_ctx ctx;
|
||||
unsigned char hash[SHA_DIGEST_LENGTH];
|
||||
|
||||
if(SHA_DIGEST_LENGTH != node->name_len) {
|
||||
/* the name hash length must be the sha1 size or
|
||||
we can't match it */
|
||||
break;
|
||||
}
|
||||
libssh2_hmac_sha1_init(&ctx, node->salt, node->salt_len);
|
||||
libssh2_hmac_update(ctx, (unsigned char *)host,
|
||||
strlen(host));
|
||||
libssh2_hmac_final(ctx, hash);
|
||||
libssh2_hmac_cleanup(&ctx);
|
||||
|
||||
if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH))
|
||||
/* this is a node we're interested in */
|
||||
match = 1;
|
||||
}
|
||||
libssh2_hmac_sha1_init(&ctx, node->salt, node->salt_len);
|
||||
libssh2_hmac_update(ctx, (unsigned char *)host, strlen(host));
|
||||
libssh2_hmac_final(ctx, hash);
|
||||
libssh2_hmac_cleanup(&ctx);
|
||||
|
||||
if(!memcmp(hash, node->name, SHA_DIGEST_LENGTH))
|
||||
/* this is a node we're interested in */
|
||||
match = 1;
|
||||
}
|
||||
break;
|
||||
default: /* unsupported type */
|
||||
break;
|
||||
}
|
||||
if(match) {
|
||||
/* host name match, now compare the keys */
|
||||
if(!strcmp(key, node->key)) {
|
||||
/* they match! */
|
||||
*ext = knownhost_to_external(node);
|
||||
badkey = NULL;
|
||||
rc = LIBSSH2_KNOWNHOST_CHECK_MATCH;
|
||||
break;
|
||||
default: /* unsupported type */
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* remember the first node that had a host match but a failed
|
||||
key match since we continue our search from here */
|
||||
if(!badkey)
|
||||
badkey = node;
|
||||
if(match) {
|
||||
/* host name match, now compare the keys */
|
||||
if(!strcmp(key, node->key)) {
|
||||
/* they match! */
|
||||
*ext = knownhost_to_external(node);
|
||||
badkey = NULL;
|
||||
rc = LIBSSH2_KNOWNHOST_CHECK_MATCH;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* remember the first node that had a host match but a
|
||||
failed key match since we continue our search from
|
||||
here */
|
||||
if(!badkey)
|
||||
badkey = node;
|
||||
match = 0; /* don't count this as a match anymore */
|
||||
}
|
||||
}
|
||||
node= _libssh2_list_next(&node->node);
|
||||
}
|
||||
node= _libssh2_list_next(&node->node);
|
||||
}
|
||||
host = hostp;
|
||||
} while(!match && --numcheck);
|
||||
|
||||
if(badkey) {
|
||||
/* key mismatch */
|
||||
@ -425,6 +451,69 @@ libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* libssh2_knownhost_check
|
||||
*
|
||||
* Check a host and its associated key against the collection of known hosts.
|
||||
*
|
||||
* The typemask is the type/format of the given host name and key
|
||||
*
|
||||
* plain - ascii "hostname.domain.tld"
|
||||
* sha1 - NOT SUPPORTED AS INPUT
|
||||
* custom - prehashed base64 encoded. Note that this cannot use any salts.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* LIBSSH2_KNOWNHOST_CHECK_FAILURE
|
||||
* LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
|
||||
* LIBSSH2_KNOWNHOST_CHECK_MATCH
|
||||
* LIBSSH2_KNOWNHOST_CHECK_MISMATCH
|
||||
*/
|
||||
LIBSSH2_API int
|
||||
libssh2_knownhost_check(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
const char *hostp, const char *key, size_t keylen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **ext)
|
||||
{
|
||||
return knownhost_check(hosts, hostp, 0, key, keylen,
|
||||
typemask, ext);
|
||||
}
|
||||
|
||||
/*
|
||||
* libssh2_knownhost_checkp
|
||||
*
|
||||
* Check a host+port and its associated key against the collection of known
|
||||
* hosts.
|
||||
*
|
||||
* Note that if 'port' is specified as non-zero, the check function will be
|
||||
* able to check for a dedicated key for this particular host+port combo, and
|
||||
* if 'port' is set to zero it only checks for the generic host key.
|
||||
*
|
||||
* The typemask is the type/format of the given host name and key
|
||||
*
|
||||
* plain - ascii "hostname.domain.tld"
|
||||
* sha1 - NOT SUPPORTED AS INPUT
|
||||
* custom - prehashed base64 encoded. Note that this cannot use any salts.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* LIBSSH2_KNOWNHOST_CHECK_FAILURE
|
||||
* LIBSSH2_KNOWNHOST_CHECK_NOTFOUND
|
||||
* LIBSSH2_KNOWNHOST_CHECK_MATCH
|
||||
* LIBSSH2_KNOWNHOST_CHECK_MISMATCH
|
||||
*/
|
||||
LIBSSH2_API int
|
||||
libssh2_knownhost_checkp(LIBSSH2_KNOWNHOSTS *hosts,
|
||||
const char *hostp, int port,
|
||||
const char *key, size_t keylen,
|
||||
int typemask,
|
||||
struct libssh2_knownhost **ext)
|
||||
{
|
||||
return knownhost_check(hosts, hostp, port, key, keylen,
|
||||
typemask, ext);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* libssh2_knownhost_del
|
||||
*
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user