1
0
mirror of https://github.com/libssh2/libssh2.git synced 2025-10-29 00:54:50 +03:00
Files
libssh2/src/hostkey.c
Viktor Szakats ec0feae792 build: speed up and extend picky compiler options
Implement picky warnings with clang in autotools. Extend picky gcc
warnings, sync them between build tools and compilers and greatly
speed up detection in CMake.

- autotools: enable clang compiler warnings with `--enable-debug`.

- autotools: enable more gcc compiler warnings with `--enable-debug`.

- autotools/cmake: sync compiler warning options between gcc and clang.

- sync compiler warning options between autotools and cmake.

- cmake: reduce option-checks to speed up the detection phase.
  Bring them down to 3 (from 35). Leaving some checks to keep the
  CMake logic alive and for an easy way to add new options.

  clang 3.0 (2011-11-29) and gcc 2.95 (1999-07-31) now required.

- autotools logic copied from curl, with these differences:

  - delete `-Wimplicit-fallthrough=4` due to a false positive.

  - reduce `-Wformat-truncation=2` to `1` due to a false positive.

  - simplify MinGW detection for `-Wno-pedantic-ms-format`.

- cmake: show enabled picky compiler options (like autotools).

- cmake: do compile `tests/simple.c` and `tests/ssh2.c`.

- fix new compiler warnings.

- `tests/CMakeLists.txt`: fix indentation.

Original source of autotools logic:
- a8fbdb461c/acinclude.m4
- a8fbdb461c/m4/curl-compilers.m4

Notice that the autotools implementation considers Apple clang as
legacy clang 3.7. CMake detection works more accurately, at the same
time more error-prone and difficult to update due to the sparsely
documented nature of Apple clang option evolution.

Closes #952
2023-04-13 11:12:22 +00:00

1390 lines
38 KiB
C

/* Copyright (c) 2004-2006, Sara Golemon <sarag@libssh2.org>
* Copyright (c) 2009-2019 by Daniel Stenberg
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* with or without modification, are permitted provided
* that the following conditions are met:
*
* Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the copyright holder nor the names
* of any other contributors may be used to endorse or
* promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*/
#include "libssh2_priv.h"
/* Needed for struct iovec on some platforms */
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#if LIBSSH2_RSA
/* ***********
* ssh-rsa *
*********** */
static int hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session,
void **abstract);
/*
* hostkey_method_ssh_rsa_init
*
* Initialize the server hostkey working area with e/n pair
*/
static int
hostkey_method_ssh_rsa_init(LIBSSH2_SESSION * session,
const unsigned char *hostkey_data,
size_t hostkey_data_len,
void **abstract)
{
libssh2_rsa_ctx *rsactx;
unsigned char *e, *n, *type;
size_t e_len, n_len, type_len;
struct string_buf buf;
if(*abstract) {
hostkey_method_ssh_rsa_dtor(session, abstract);
*abstract = NULL;
}
if(hostkey_data_len < 19) {
_libssh2_debug((session, LIBSSH2_TRACE_ERROR,
"host key length too short"));
return -1;
}
buf.data = (unsigned char *)hostkey_data;
buf.dataptr = buf.data;
buf.len = hostkey_data_len;
if(_libssh2_get_string(&buf, &type, &type_len)) {
return -1;
}
/* we accept one of 3 header types */
if(type_len == 7 && strncmp("ssh-rsa", (char *)type, 7) == 0) {
/* ssh-rsa */
}
#if LIBSSH2_RSA_SHA2
else if(type_len == 12 && strncmp("rsa-sha2-256", (char *)type, 12) == 0) {
/* rsa-sha2-256 */
}
else if(type_len == 12 && strncmp("rsa-sha2-512", (char *)type, 12) == 0) {
/* rsa-sha2-512 */
}
#endif
else {
_libssh2_debug((session, LIBSSH2_TRACE_ERROR,
"unexpected rsa type: %.*s", type_len, type));
return -1;
}
if(_libssh2_get_string(&buf, &e, &e_len))
return -1;
if(_libssh2_get_string(&buf, &n, &n_len))
return -1;
if(!_libssh2_eob(&buf))
return -1;
if(_libssh2_rsa_new(&rsactx,
e, (unsigned long)e_len,
n, (unsigned long)n_len,
NULL, 0, NULL, 0, NULL, 0,
NULL, 0, NULL, 0, NULL, 0)) {
return -1;
}
*abstract = rsactx;
return 0;
}
/*
* hostkey_method_ssh_rsa_initPEM
*
* Load a Private Key from a PEM file
*/
static int
hostkey_method_ssh_rsa_initPEM(LIBSSH2_SESSION * session,
const char *privkeyfile,
unsigned const char *passphrase,
void **abstract)
{
libssh2_rsa_ctx *rsactx;
int ret;
if(*abstract) {
hostkey_method_ssh_rsa_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_rsa_new_private(&rsactx, session, privkeyfile, passphrase);
if(ret) {
return -1;
}
*abstract = rsactx;
return 0;
}
/*
* hostkey_method_ssh_rsa_initPEMFromMemory
*
* Load a Private Key from a memory
*/
static int
hostkey_method_ssh_rsa_initPEMFromMemory(LIBSSH2_SESSION * session,
const char *privkeyfiledata,
size_t privkeyfiledata_len,
unsigned const char *passphrase,
void **abstract)
{
libssh2_rsa_ctx *rsactx;
int ret;
if(*abstract) {
hostkey_method_ssh_rsa_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_rsa_new_private_frommemory(&rsactx, session,
privkeyfiledata,
privkeyfiledata_len, passphrase);
if(ret) {
return -1;
}
*abstract = rsactx;
return 0;
}
/*
* hostkey_method_ssh_rsa_sign
*
* Verify signature created by remote
*/
static int
hostkey_method_ssh_rsa_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
(void) session;
/* Skip past keyname_len(4) + keyname(7){"ssh-rsa"} + signature_len(4) */
if(sig_len < 15)
return -1;
sig += 15;
sig_len -= 15;
return _libssh2_rsa_sha1_verify(rsactx, sig, sig_len, m, m_len);
}
/*
* hostkey_method_ssh_rsa_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_rsa_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
#ifdef _libssh2_rsa_sha1_signv
return _libssh2_rsa_sha1_signv(session, signature, signature_len,
veccount, datavec, rsactx);
#else
int ret;
int i;
unsigned char hash[SHA_DIGEST_LENGTH];
libssh2_sha1_ctx ctx;
(void)libssh2_sha1_init(&ctx);
for(i = 0; i < veccount; i++) {
libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
}
libssh2_sha1_final(ctx, hash);
ret = _libssh2_rsa_sha1_sign(session, rsactx, hash, SHA_DIGEST_LENGTH,
signature, signature_len);
if(ret) {
return -1;
}
return 0;
#endif
}
/*
* hostkey_method_ssh_rsa_sha2_256_sig_verify
*
* Verify signature created by remote
*/
#if LIBSSH2_RSA_SHA2
static int
hostkey_method_ssh_rsa_sha2_256_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
(void) session;
/* Skip past keyname_len(4) + keyname(12){"rsa-sha2-256"} +
signature_len(4) */
if(sig_len < 20)
return -1;
sig += 20;
sig_len -= 20;
return _libssh2_rsa_sha2_verify(rsactx, SHA256_DIGEST_LENGTH, sig, sig_len,
m, m_len);
}
/*
* hostkey_method_ssh_rsa_sha2_256_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_rsa_sha2_256_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
#ifdef _libssh2_rsa_sha2_256_signv
return _libssh2_rsa_sha2_256_signv(session, signature, signature_len,
veccount, datavec, rsactx);
#else
int ret;
int i;
unsigned char hash[SHA256_DIGEST_LENGTH];
libssh2_sha256_ctx ctx;
if(!libssh2_sha256_init(&ctx)) {
return -1;
}
for(i = 0; i < veccount; i++) {
libssh2_sha256_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
}
libssh2_sha256_final(ctx, hash);
ret = _libssh2_rsa_sha2_sign(session, rsactx, hash, SHA256_DIGEST_LENGTH,
signature, signature_len);
if(ret) {
return -1;
}
return 0;
#endif
}
/*
* hostkey_method_ssh_rsa_sha2_512_sig_verify
*
* Verify signature created by remote
*/
static int
hostkey_method_ssh_rsa_sha2_512_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
(void) session;
/* Skip past keyname_len(4) + keyname(12){"rsa-sha2-512"} +
signature_len(4) */
if(sig_len < 20)
return -1;
sig += 20;
sig_len -= 20;
return _libssh2_rsa_sha2_verify(rsactx, SHA512_DIGEST_LENGTH, sig,
sig_len, m, m_len);
}
/*
* hostkey_method_ssh_rsa_sha2_512_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_rsa_sha2_512_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
#ifdef _libssh2_rsa_sha2_512_signv
return _libssh2_rsa_sha2_512_signv(session, signature, signature_len,
veccount, datavec, rsactx);
#else
int ret;
int i;
unsigned char hash[SHA512_DIGEST_LENGTH];
libssh2_sha512_ctx ctx;
if(!libssh2_sha512_init(&ctx)) {
return -1;
}
for(i = 0; i < veccount; i++) {
libssh2_sha512_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
}
libssh2_sha512_final(ctx, hash);
ret = _libssh2_rsa_sha2_sign(session, rsactx, hash, SHA512_DIGEST_LENGTH,
signature, signature_len);
if(ret) {
return -1;
}
return 0;
#endif
}
#endif /* LIBSSH2_RSA_SHA2 */
/*
* hostkey_method_ssh_rsa_dtor
*
* Shutdown the hostkey
*/
static int
hostkey_method_ssh_rsa_dtor(LIBSSH2_SESSION * session, void **abstract)
{
libssh2_rsa_ctx *rsactx = (libssh2_rsa_ctx *) (*abstract);
(void) session;
_libssh2_rsa_free(rsactx);
*abstract = NULL;
return 0;
}
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa = {
"ssh-rsa",
SHA_DIGEST_LENGTH,
hostkey_method_ssh_rsa_init,
hostkey_method_ssh_rsa_initPEM,
hostkey_method_ssh_rsa_initPEMFromMemory,
hostkey_method_ssh_rsa_sig_verify,
hostkey_method_ssh_rsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_rsa_dtor,
};
#if LIBSSH2_RSA_SHA2
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_sha2_256 = {
"rsa-sha2-256",
SHA256_DIGEST_LENGTH,
hostkey_method_ssh_rsa_init,
hostkey_method_ssh_rsa_initPEM,
hostkey_method_ssh_rsa_initPEMFromMemory,
hostkey_method_ssh_rsa_sha2_256_sig_verify,
hostkey_method_ssh_rsa_sha2_256_signv,
NULL, /* encrypt */
hostkey_method_ssh_rsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_sha2_512 = {
"rsa-sha2-512",
SHA512_DIGEST_LENGTH,
hostkey_method_ssh_rsa_init,
hostkey_method_ssh_rsa_initPEM,
hostkey_method_ssh_rsa_initPEMFromMemory,
hostkey_method_ssh_rsa_sha2_512_sig_verify,
hostkey_method_ssh_rsa_sha2_512_signv,
NULL, /* encrypt */
hostkey_method_ssh_rsa_dtor,
};
#endif /* LIBSSH2_RSA_SHA2 */
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_rsa_cert = {
"ssh-rsa-cert-v01@openssh.com",
SHA_DIGEST_LENGTH,
NULL,
hostkey_method_ssh_rsa_initPEM,
hostkey_method_ssh_rsa_initPEMFromMemory,
NULL,
hostkey_method_ssh_rsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_rsa_dtor,
};
#endif /* LIBSSH2_RSA */
#if LIBSSH2_DSA
/* ***********
* ssh-dss *
*********** */
static int hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session,
void **abstract);
/*
* hostkey_method_ssh_dss_init
*
* Initialize the server hostkey working area with p/q/g/y set
*/
static int
hostkey_method_ssh_dss_init(LIBSSH2_SESSION * session,
const unsigned char *hostkey_data,
size_t hostkey_data_len,
void **abstract)
{
libssh2_dsa_ctx *dsactx;
unsigned char *p, *q, *g, *y;
size_t p_len, q_len, g_len, y_len;
struct string_buf buf;
if(*abstract) {
hostkey_method_ssh_dss_dtor(session, abstract);
*abstract = NULL;
}
if(hostkey_data_len < 27) {
_libssh2_debug((session, LIBSSH2_TRACE_ERROR,
"host key length too short"));
return -1;
}
buf.data = (unsigned char *)hostkey_data;
buf.dataptr = buf.data;
buf.len = hostkey_data_len;
if(_libssh2_match_string(&buf, "ssh-dss"))
return -1;
if(_libssh2_get_string(&buf, &p, &p_len))
return -1;
if(_libssh2_get_string(&buf, &q, &q_len))
return -1;
if(_libssh2_get_string(&buf, &g, &g_len))
return -1;
if(_libssh2_get_string(&buf, &y, &y_len))
return -1;
if(!_libssh2_eob(&buf))
return -1;
if(_libssh2_dsa_new(&dsactx,
p, (unsigned long)p_len,
q, (unsigned long)q_len,
g, (unsigned long)g_len,
y, (unsigned long)y_len,
NULL, 0)) {
return -1;
}
*abstract = dsactx;
return 0;
}
/*
* hostkey_method_ssh_dss_initPEM
*
* Load a Private Key from a PEM file
*/
static int
hostkey_method_ssh_dss_initPEM(LIBSSH2_SESSION * session,
const char *privkeyfile,
unsigned const char *passphrase,
void **abstract)
{
libssh2_dsa_ctx *dsactx;
int ret;
if(*abstract) {
hostkey_method_ssh_dss_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_dsa_new_private(&dsactx, session, privkeyfile, passphrase);
if(ret) {
return -1;
}
*abstract = dsactx;
return 0;
}
/*
* hostkey_method_ssh_dss_initPEMFromMemory
*
* Load a Private Key from memory
*/
static int
hostkey_method_ssh_dss_initPEMFromMemory(LIBSSH2_SESSION * session,
const char *privkeyfiledata,
size_t privkeyfiledata_len,
unsigned const char *passphrase,
void **abstract)
{
libssh2_dsa_ctx *dsactx;
int ret;
if(*abstract) {
hostkey_method_ssh_dss_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_dsa_new_private_frommemory(&dsactx, session,
privkeyfiledata,
privkeyfiledata_len, passphrase);
if(ret) {
return -1;
}
*abstract = dsactx;
return 0;
}
/*
* libssh2_hostkey_method_ssh_dss_sign
*
* Verify signature created by remote
*/
static int
hostkey_method_ssh_dss_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
/* Skip past keyname_len(4) + keyname(7){"ssh-dss"} + signature_len(4) */
if(sig_len != 55) {
return _libssh2_error(session, LIBSSH2_ERROR_PROTO,
"Invalid DSS signature length");
}
sig += 15;
sig_len -= 15;
return _libssh2_dsa_sha1_verify(dsactx, sig, m, m_len);
}
/*
* hostkey_method_ssh_dss_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_dss_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
unsigned char hash[SHA_DIGEST_LENGTH];
libssh2_sha1_ctx ctx;
int i;
*signature = LIBSSH2_CALLOC(session, 2 * SHA_DIGEST_LENGTH);
if(!*signature) {
return -1;
}
*signature_len = 2 * SHA_DIGEST_LENGTH;
(void)libssh2_sha1_init(&ctx);
for(i = 0; i < veccount; i++) {
libssh2_sha1_update(ctx, datavec[i].iov_base, datavec[i].iov_len);
}
libssh2_sha1_final(ctx, hash);
if(_libssh2_dsa_sha1_sign(dsactx, hash, SHA_DIGEST_LENGTH, *signature)) {
LIBSSH2_FREE(session, *signature);
return -1;
}
return 0;
}
/*
* libssh2_hostkey_method_ssh_dss_dtor
*
* Shutdown the hostkey method
*/
static int
hostkey_method_ssh_dss_dtor(LIBSSH2_SESSION * session, void **abstract)
{
libssh2_dsa_ctx *dsactx = (libssh2_dsa_ctx *) (*abstract);
(void) session;
_libssh2_dsa_free(dsactx);
*abstract = NULL;
return 0;
}
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_dss = {
"ssh-dss",
SHA_DIGEST_LENGTH,
hostkey_method_ssh_dss_init,
hostkey_method_ssh_dss_initPEM,
hostkey_method_ssh_dss_initPEMFromMemory,
hostkey_method_ssh_dss_sig_verify,
hostkey_method_ssh_dss_signv,
NULL, /* encrypt */
hostkey_method_ssh_dss_dtor,
};
#endif /* LIBSSH2_DSA */
#if LIBSSH2_ECDSA
/* ***********
* ecdsa-sha2-nistp256/384/521 *
*********** */
static int
hostkey_method_ssh_ecdsa_dtor(LIBSSH2_SESSION * session,
void **abstract);
/*
* hostkey_method_ssh_ecdsa_init
*
* Initialize the server hostkey working area with e/n pair
*/
static int
hostkey_method_ssh_ecdsa_init(LIBSSH2_SESSION * session,
const unsigned char *hostkey_data,
size_t hostkey_data_len,
void **abstract)
{
libssh2_ecdsa_ctx *ecdsactx = NULL;
unsigned char *type_str, *domain, *public_key;
size_t key_len, len;
libssh2_curve_type type;
struct string_buf buf;
if(abstract != NULL && *abstract) {
hostkey_method_ssh_ecdsa_dtor(session, abstract);
*abstract = NULL;
}
if(hostkey_data_len < 39) {
_libssh2_debug((session, LIBSSH2_TRACE_ERROR,
"host key length too short"));
return -1;
}
buf.data = (unsigned char *)hostkey_data;
buf.dataptr = buf.data;
buf.len = hostkey_data_len;
if(_libssh2_get_string(&buf, &type_str, &len) || len != 19)
return -1;
if(strncmp((char *) type_str, "ecdsa-sha2-nistp256", 19) == 0) {
type = LIBSSH2_EC_CURVE_NISTP256;
}
else if(strncmp((char *) type_str, "ecdsa-sha2-nistp384", 19) == 0) {
type = LIBSSH2_EC_CURVE_NISTP384;
}
else if(strncmp((char *) type_str, "ecdsa-sha2-nistp521", 19) == 0) {
type = LIBSSH2_EC_CURVE_NISTP521;
}
else {
return -1;
}
if(_libssh2_get_string(&buf, &domain, &len) || len != 8)
return -1;
if(type == LIBSSH2_EC_CURVE_NISTP256 &&
strncmp((char *)domain, "nistp256", 8) != 0) {
return -1;
}
else if(type == LIBSSH2_EC_CURVE_NISTP384 &&
strncmp((char *)domain, "nistp384", 8) != 0) {
return -1;
}
else if(type == LIBSSH2_EC_CURVE_NISTP521 &&
strncmp((char *)domain, "nistp521", 8) != 0) {
return -1;
}
/* public key */
if(_libssh2_get_string(&buf, &public_key, &key_len))
return -1;
if(!_libssh2_eob(&buf))
return -1;
if(_libssh2_ecdsa_curve_name_with_octal_new(&ecdsactx, public_key,
key_len, type))
return -1;
if(abstract != NULL)
*abstract = ecdsactx;
return 0;
}
/*
* hostkey_method_ssh_ecdsa_initPEM
*
* Load a Private Key from a PEM file
*/
static int
hostkey_method_ssh_ecdsa_initPEM(LIBSSH2_SESSION * session,
const char *privkeyfile,
unsigned const char *passphrase,
void **abstract)
{
libssh2_ecdsa_ctx *ec_ctx = NULL;
int ret;
if(abstract != NULL && *abstract) {
hostkey_method_ssh_ecdsa_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_ecdsa_new_private(&ec_ctx, session,
privkeyfile, passphrase);
if(abstract != NULL)
*abstract = ec_ctx;
return ret;
}
/*
* hostkey_method_ssh_ecdsa_initPEMFromMemory
*
* Load a Private Key from memory
*/
static int
hostkey_method_ssh_ecdsa_initPEMFromMemory(LIBSSH2_SESSION * session,
const char *privkeyfiledata,
size_t privkeyfiledata_len,
unsigned const char *passphrase,
void **abstract)
{
libssh2_ecdsa_ctx *ec_ctx = NULL;
int ret;
if(abstract != NULL && *abstract) {
hostkey_method_ssh_ecdsa_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_ecdsa_new_private_frommemory(&ec_ctx, session,
privkeyfiledata,
privkeyfiledata_len,
passphrase);
if(ret) {
return -1;
}
if(abstract != NULL)
*abstract = ec_ctx;
return 0;
}
/*
* hostkey_method_ecdsa_sig_verify
*
* Verify signature created by remote
*/
static int
hostkey_method_ssh_ecdsa_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
unsigned char *r, *s, *name;
size_t r_len, s_len, name_len;
uint32_t len;
struct string_buf buf;
libssh2_ecdsa_ctx *ctx = (libssh2_ecdsa_ctx *) (*abstract);
(void) session;
if(sig_len < 35)
return -1;
/* keyname_len(4) + keyname(19){"ecdsa-sha2-nistp256"} +
signature_len(4) */
buf.data = (unsigned char *)sig;
buf.dataptr = buf.data;
buf.len = sig_len;
if(_libssh2_get_string(&buf, &name, &name_len) || name_len != 19)
return -1;
if(_libssh2_get_u32(&buf, &len) != 0 || len < 8)
return -1;
if(_libssh2_get_string(&buf, &r, &r_len))
return -1;
if(_libssh2_get_string(&buf, &s, &s_len))
return -1;
return _libssh2_ecdsa_verify(ctx, r, r_len, s, s_len, m, m_len);
}
#define LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(digest_type) \
do { \
unsigned char hash[SHA##digest_type##_DIGEST_LENGTH]; \
libssh2_sha##digest_type##_ctx ctx; \
int i; \
(void)libssh2_sha##digest_type##_init(&ctx); \
for(i = 0; i < veccount; i++) { \
libssh2_sha##digest_type##_update(ctx, datavec[i].iov_base, \
datavec[i].iov_len); \
} \
libssh2_sha##digest_type##_final(ctx, hash); \
ret = _libssh2_ecdsa_sign(session, ec_ctx, hash, \
SHA##digest_type##_DIGEST_LENGTH, \
signature, signature_len); \
} while(0)
/*
* hostkey_method_ecdsa_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_ecdsa_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_ecdsa_ctx *ec_ctx = (libssh2_ecdsa_ctx *) (*abstract);
libssh2_curve_type type = _libssh2_ecdsa_get_curve_type(ec_ctx);
int ret = 0;
if(type == LIBSSH2_EC_CURVE_NISTP256) {
LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(256);
}
else if(type == LIBSSH2_EC_CURVE_NISTP384) {
LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(384);
}
else if(type == LIBSSH2_EC_CURVE_NISTP521) {
LIBSSH2_HOSTKEY_METHOD_EC_SIGNV_HASH(512);
}
else {
return -1;
}
return ret;
}
/*
* hostkey_method_ssh_ecdsa_dtor
*
* Shutdown the hostkey by freeing EC_KEY context
*/
static int
hostkey_method_ssh_ecdsa_dtor(LIBSSH2_SESSION * session, void **abstract)
{
libssh2_ecdsa_ctx *keyctx = (libssh2_ecdsa_ctx *) (*abstract);
(void) session;
if(keyctx != NULL)
_libssh2_ecdsa_free(keyctx);
*abstract = NULL;
return 0;
}
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp256 = {
"ecdsa-sha2-nistp256",
SHA256_DIGEST_LENGTH,
hostkey_method_ssh_ecdsa_init,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
hostkey_method_ssh_ecdsa_sig_verify,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp384 = {
"ecdsa-sha2-nistp384",
SHA384_DIGEST_LENGTH,
hostkey_method_ssh_ecdsa_init,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
hostkey_method_ssh_ecdsa_sig_verify,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521 = {
"ecdsa-sha2-nistp521",
SHA512_DIGEST_LENGTH,
hostkey_method_ssh_ecdsa_init,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
hostkey_method_ssh_ecdsa_sig_verify,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp256_cert = {
"ecdsa-sha2-nistp256-cert-v01@openssh.com",
SHA256_DIGEST_LENGTH,
NULL,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
NULL,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp384_cert = {
"ecdsa-sha2-nistp384-cert-v01@openssh.com",
SHA384_DIGEST_LENGTH,
NULL,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
NULL,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ecdsa_ssh_nistp521_cert = {
"ecdsa-sha2-nistp521-cert-v01@openssh.com",
SHA512_DIGEST_LENGTH,
NULL,
hostkey_method_ssh_ecdsa_initPEM,
hostkey_method_ssh_ecdsa_initPEMFromMemory,
NULL,
hostkey_method_ssh_ecdsa_signv,
NULL, /* encrypt */
hostkey_method_ssh_ecdsa_dtor,
};
#endif /* LIBSSH2_ECDSA */
#if LIBSSH2_ED25519
/* ***********
* ed25519 *
*********** */
static int hostkey_method_ssh_ed25519_dtor(LIBSSH2_SESSION * session,
void **abstract);
/*
* hostkey_method_ssh_ed25519_init
*
* Initialize the server hostkey working area with e/n pair
*/
static int
hostkey_method_ssh_ed25519_init(LIBSSH2_SESSION * session,
const unsigned char *hostkey_data,
size_t hostkey_data_len,
void **abstract)
{
size_t key_len;
unsigned char *key;
libssh2_ed25519_ctx *ctx = NULL;
struct string_buf buf;
if(*abstract) {
hostkey_method_ssh_ed25519_dtor(session, abstract);
*abstract = NULL;
}
if(hostkey_data_len < 19) {
_libssh2_debug((session, LIBSSH2_TRACE_ERROR,
"host key length too short"));
return -1;
}
buf.data = (unsigned char *)hostkey_data;
buf.dataptr = buf.data;
buf.len = hostkey_data_len;
if(_libssh2_match_string(&buf, "ssh-ed25519"))
return -1;
/* public key */
if(_libssh2_get_string(&buf, &key, &key_len))
return -1;
if(!_libssh2_eob(&buf))
return -1;
if(_libssh2_ed25519_new_public(&ctx, session, key, key_len) != 0) {
return -1;
}
*abstract = ctx;
return 0;
}
/*
* hostkey_method_ssh_ed25519_initPEM
*
* Load a Private Key from a PEM file
*/
static int
hostkey_method_ssh_ed25519_initPEM(LIBSSH2_SESSION * session,
const char *privkeyfile,
unsigned const char *passphrase,
void **abstract)
{
libssh2_ed25519_ctx *ec_ctx = NULL;
int ret;
if(*abstract) {
hostkey_method_ssh_ed25519_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_ed25519_new_private(&ec_ctx, session,
privkeyfile, passphrase);
if(ret) {
return -1;
}
*abstract = ec_ctx;
return ret;
}
/*
* hostkey_method_ssh_ed25519_initPEMFromMemory
*
* Load a Private Key from memory
*/
static int
hostkey_method_ssh_ed25519_initPEMFromMemory(LIBSSH2_SESSION * session,
const char *privkeyfiledata,
size_t privkeyfiledata_len,
unsigned const char *passphrase,
void **abstract)
{
libssh2_ed25519_ctx *ed_ctx = NULL;
int ret;
if(abstract != NULL && *abstract) {
hostkey_method_ssh_ed25519_dtor(session, abstract);
*abstract = NULL;
}
ret = _libssh2_ed25519_new_private_frommemory(&ed_ctx, session,
privkeyfiledata,
privkeyfiledata_len,
passphrase);
if(ret) {
return -1;
}
if(abstract != NULL)
*abstract = ed_ctx;
return 0;
}
/*
* hostkey_method_ssh_ed25519_sig_verify
*
* Verify signature created by remote
*/
static int
hostkey_method_ssh_ed25519_sig_verify(LIBSSH2_SESSION * session,
const unsigned char *sig,
size_t sig_len,
const unsigned char *m,
size_t m_len, void **abstract)
{
libssh2_ed25519_ctx *ctx = (libssh2_ed25519_ctx *) (*abstract);
(void) session;
if(sig_len < 19)
return -1;
/* Skip past keyname_len(4) + keyname(11){"ssh-ed25519"} +
signature_len(4) */
sig += 19;
sig_len -= 19;
if(sig_len != LIBSSH2_ED25519_SIG_LEN)
return -1;
return _libssh2_ed25519_verify(ctx, sig, sig_len, m, m_len);
}
/*
* hostkey_method_ssh_ed25519_signv
*
* Construct a signature from an array of vectors
*/
static int
hostkey_method_ssh_ed25519_signv(LIBSSH2_SESSION * session,
unsigned char **signature,
size_t *signature_len,
int veccount,
const struct iovec datavec[],
void **abstract)
{
libssh2_ed25519_ctx *ctx = (libssh2_ed25519_ctx *) (*abstract);
if(veccount != 1) {
return -1;
}
return _libssh2_ed25519_sign(ctx, session, signature, signature_len,
datavec[0].iov_base, datavec[0].iov_len);
}
/*
* hostkey_method_ssh_ed25519_dtor
*
* Shutdown the hostkey by freeing key context
*/
static int
hostkey_method_ssh_ed25519_dtor(LIBSSH2_SESSION * session, void **abstract)
{
libssh2_ed25519_ctx *keyctx = (libssh2_ed25519_ctx*) (*abstract);
(void) session;
if(keyctx)
_libssh2_ed25519_free(keyctx);
*abstract = NULL;
return 0;
}
static const LIBSSH2_HOSTKEY_METHOD hostkey_method_ssh_ed25519 = {
"ssh-ed25519",
SHA256_DIGEST_LENGTH,
hostkey_method_ssh_ed25519_init,
hostkey_method_ssh_ed25519_initPEM,
hostkey_method_ssh_ed25519_initPEMFromMemory,
hostkey_method_ssh_ed25519_sig_verify,
hostkey_method_ssh_ed25519_signv,
NULL, /* encrypt */
hostkey_method_ssh_ed25519_dtor,
};
#endif /* LIBSSH2_ED25519 */
static const LIBSSH2_HOSTKEY_METHOD *hostkey_methods[] = {
#if LIBSSH2_ECDSA
&hostkey_method_ecdsa_ssh_nistp256,
&hostkey_method_ecdsa_ssh_nistp384,
&hostkey_method_ecdsa_ssh_nistp521,
&hostkey_method_ecdsa_ssh_nistp256_cert,
&hostkey_method_ecdsa_ssh_nistp384_cert,
&hostkey_method_ecdsa_ssh_nistp521_cert,
#endif
#if LIBSSH2_ED25519
&hostkey_method_ssh_ed25519,
#endif
#if LIBSSH2_RSA
#if LIBSSH2_RSA_SHA2
&hostkey_method_ssh_rsa_sha2_512,
&hostkey_method_ssh_rsa_sha2_256,
#endif /* LIBSSH2_RSA_SHA2 */
&hostkey_method_ssh_rsa,
&hostkey_method_ssh_rsa_cert,
#endif /* LIBSSH2_RSA */
#if LIBSSH2_DSA
&hostkey_method_ssh_dss,
#endif /* LIBSSH2_DSA */
NULL
};
const LIBSSH2_HOSTKEY_METHOD **
libssh2_hostkey_methods(void)
{
return hostkey_methods;
}
/*
* libssh2_hostkey_hash
*
* Returns hash signature
* Returned buffer should NOT be freed
* Length of buffer is determined by hash type
* i.e. MD5 == 16, SHA1 == 20, SHA256 == 32
*/
LIBSSH2_API const char *
libssh2_hostkey_hash(LIBSSH2_SESSION * session, int hash_type)
{
switch(hash_type) {
#if LIBSSH2_MD5
case LIBSSH2_HOSTKEY_HASH_MD5:
return (session->server_hostkey_md5_valid)
? (char *) session->server_hostkey_md5
: NULL;
break;
#endif /* LIBSSH2_MD5 */
case LIBSSH2_HOSTKEY_HASH_SHA1:
return (session->server_hostkey_sha1_valid)
? (char *) session->server_hostkey_sha1
: NULL;
break;
case LIBSSH2_HOSTKEY_HASH_SHA256:
return (session->server_hostkey_sha256_valid)
? (char *) session->server_hostkey_sha256
: NULL;
break;
default:
return NULL;
}
}
static int hostkey_type(const unsigned char *hostkey, size_t len)
{
static const unsigned char rsa[] = {
0, 0, 0, 0x07, 's', 's', 'h', '-', 'r', 's', 'a'
};
static const unsigned char dss[] = {
0, 0, 0, 0x07, 's', 's', 'h', '-', 'd', 's', 's'
};
static const unsigned char ecdsa_256[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
'n', 'i', 's', 't', 'p', '2', '5', '6'
};
static const unsigned char ecdsa_384[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
'n', 'i', 's', 't', 'p', '3', '8', '4'
};
static const unsigned char ecdsa_521[] = {
0, 0, 0, 0x13, 'e', 'c', 'd', 's', 'a', '-', 's', 'h', 'a', '2', '-',
'n', 'i', 's', 't', 'p', '5', '2', '1'
};
static const unsigned char ed25519[] = {
0, 0, 0, 0x0b, 's', 's', 'h', '-', 'e', 'd', '2', '5', '5', '1', '9'
};
if(len < 11)
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
if(!memcmp(rsa, hostkey, 11))
return LIBSSH2_HOSTKEY_TYPE_RSA;
if(!memcmp(dss, hostkey, 11))
return LIBSSH2_HOSTKEY_TYPE_DSS;
if(len < 15)
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
if(!memcmp(ed25519, hostkey, 15))
return LIBSSH2_HOSTKEY_TYPE_ED25519;
if(len < 23)
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
if(!memcmp(ecdsa_256, hostkey, 23))
return LIBSSH2_HOSTKEY_TYPE_ECDSA_256;
if(!memcmp(ecdsa_384, hostkey, 23))
return LIBSSH2_HOSTKEY_TYPE_ECDSA_384;
if(!memcmp(ecdsa_521, hostkey, 23))
return LIBSSH2_HOSTKEY_TYPE_ECDSA_521;
return LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
}
/*
* libssh2_session_hostkey()
*
* Returns the server key and length.
*
*/
LIBSSH2_API const char *
libssh2_session_hostkey(LIBSSH2_SESSION *session, size_t *len, int *type)
{
if(session->server_hostkey_len) {
if(len)
*len = session->server_hostkey_len;
if(type)
*type = hostkey_type(session->server_hostkey,
session->server_hostkey_len);
return (char *) session->server_hostkey;
}
if(len)
*len = 0;
return NULL;
}