libssh2: add SHA256 fingerprint support
Added support for SHA256 fingerprint in command line curl and in libcurl. Closes #7646
This commit is contained in:
Родитель
1ca62bb5ce
Коммит
d1e7d9197b
10
docs/TODO
10
docs/TODO
|
@ -138,7 +138,6 @@
|
|||
17. SSH protocols
|
||||
17.1 Multiplexing
|
||||
17.2 Handle growing SFTP files
|
||||
17.3 Support better than MD5 hostkey hash
|
||||
17.4 Support CURLOPT_PREQUOTE
|
||||
17.5 SSH over HTTPS proxy with more backends
|
||||
|
||||
|
@ -930,15 +929,6 @@
|
|||
|
||||
https://github.com/curl/curl/issues/4344
|
||||
|
||||
17.3 Support better than MD5 hostkey hash
|
||||
|
||||
libcurl offers the CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 option for verifying the
|
||||
server's key. MD5 is generally being deprecated so we should implement
|
||||
support for stronger hashing algorithms. libssh2 itself is what provides this
|
||||
underlying functionality and it supports at least SHA-1 as an alternative.
|
||||
SHA-1 is also being deprecated these days so we should consider working with
|
||||
libssh2 to instead offer support for SHA-256 or similar.
|
||||
|
||||
17.4 Support CURLOPT_PREQUOTE
|
||||
|
||||
The two other QUOTE options are supported for SFTP, but this was left out for
|
||||
|
|
|
@ -96,6 +96,7 @@ DPAGES = \
|
|||
header.d \
|
||||
help.d \
|
||||
hostpubmd5.d \
|
||||
hostpubsha256.d \
|
||||
hsts.d \
|
||||
http0.9.d \
|
||||
http1.0.d \
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
Long: hostpubsha256
|
||||
Arg: <sha256>
|
||||
Help: Acceptable SHA256 hash of the host public key
|
||||
Protocols: SFTP SCP
|
||||
Added: 7.80.0
|
||||
Category: sftp scp
|
||||
Example: --hostpubsha256 NDVkMTQxMGQ1ODdmMjQ3MjczYjAyOTY5MmRkMjVmNDQ= sftp://example.com/
|
||||
---
|
||||
Pass a string containing a Base64-encoded SHA256 hash of the remote
|
||||
host's public key. Curl will refuse the connection with the host
|
||||
unless the hashes match.
|
|
@ -642,6 +642,8 @@ SSH authentication types. See \fICURLOPT_SSH_AUTH_TYPES(3)\fP
|
|||
Enable SSH compression. See \fICURLOPT_SSH_COMPRESSION(3)\fP
|
||||
.IP CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
|
||||
MD5 of host's public key. See \fICURLOPT_SSH_HOST_PUBLIC_KEY_MD5(3)\fP
|
||||
.IP CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256
|
||||
SHA256 of host's public key. See \fICURLOPT_SSH_HOST_PUBLIC_KEY_SHA256(3)\fP
|
||||
.IP CURLOPT_SSH_PUBLIC_KEYFILE
|
||||
File name of public key. See \fICURLOPT_SSH_PUBLIC_KEYFILE(3)\fP
|
||||
.IP CURLOPT_SSH_PRIVATE_KEYFILE
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
.\" **************************************************************************
|
||||
.\" * _ _ ____ _
|
||||
.\" * Project ___| | | | _ \| |
|
||||
.\" * / __| | | | |_) | |
|
||||
.\" * | (__| |_| | _ <| |___
|
||||
.\" * \___|\___/|_| \_\_____|
|
||||
.\" *
|
||||
.\" * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
.\" *
|
||||
.\" * This software is licensed as described in the file COPYING, which
|
||||
.\" * you should have received as part of this distribution. The terms
|
||||
.\" * are also available at https://curl.se/docs/copyright.html.
|
||||
.\" *
|
||||
.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
.\" * copies of the Software, and permit persons to whom the Software is
|
||||
.\" * furnished to do so, under the terms of the COPYING file.
|
||||
.\" *
|
||||
.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
.\" * KIND, either express or implied.
|
||||
.\" *
|
||||
.\" **************************************************************************
|
||||
.\"
|
||||
.TH CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 3 "27 Aug 2021" "libcurl 7.80.0" "curl_easy_setopt options"
|
||||
.SH NAME
|
||||
CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 \- SHA256 hash of SSH server public key
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
#include <curl/curl.h>
|
||||
|
||||
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
|
||||
char *sha256);
|
||||
.SH DESCRIPTION
|
||||
Pass a char * pointing to a string containing a Base64-encoded SHA256
|
||||
hash of the remote host's public key.
|
||||
The transfer will fail if the given hash doesn't match the hash the
|
||||
remote host provides.
|
||||
|
||||
.SH DEFAULT
|
||||
NULL
|
||||
.SH PROTOCOLS
|
||||
SCP and SFTP
|
||||
.SH EXAMPLE
|
||||
.nf
|
||||
CURL *curl = curl_easy_init();
|
||||
if(curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "sftp://example.com/file");
|
||||
curl_easy_setopt(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
|
||||
"NDVkMTQxMGQ1ODdmMjQ3MjczYjAyOTY5MmRkMjVmNDQ=");
|
||||
ret = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
.fi
|
||||
.SH AVAILABILITY
|
||||
Added in 7.80.0
|
||||
Requires the libssh2 back-end.
|
||||
.SH RETURN VALUE
|
||||
Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or
|
||||
CURLE_OUT_OF_MEMORY if there was insufficient heap space.
|
||||
.SH "SEE ALSO"
|
||||
.BR CURLOPT_SSH_PUBLIC_KEYFILE "(3), " CURLOPT_SSH_AUTH_TYPES "(3), "
|
|
@ -326,6 +326,7 @@ man_MANS = \
|
|||
CURLOPT_SSH_AUTH_TYPES.3 \
|
||||
CURLOPT_SSH_COMPRESSION.3 \
|
||||
CURLOPT_SSH_HOST_PUBLIC_KEY_MD5.3 \
|
||||
CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.3 \
|
||||
CURLOPT_SSH_KEYDATA.3 \
|
||||
CURLOPT_SSH_KEYFUNCTION.3 \
|
||||
CURLOPT_SSH_KNOWNHOSTS.3 \
|
||||
|
|
|
@ -613,6 +613,7 @@ CURLOPT_SOURCE_USERPWD 7.12.1 - 7.15.5
|
|||
CURLOPT_SSH_AUTH_TYPES 7.16.1
|
||||
CURLOPT_SSH_COMPRESSION 7.56.0
|
||||
CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 7.17.1
|
||||
CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 7.80.0
|
||||
CURLOPT_SSH_KEYDATA 7.19.6
|
||||
CURLOPT_SSH_KEYFUNCTION 7.19.6
|
||||
CURLOPT_SSH_KNOWNHOSTS 7.19.6
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
--header (-H) 5.0
|
||||
--help (-h) 4.0
|
||||
--hostpubmd5 7.17.1
|
||||
--hostpubsha256 7.80.0
|
||||
--hsts 7.74.0
|
||||
--http0.9 7.64.0
|
||||
--http1.0 (-0) 7.9.1
|
||||
|
|
|
@ -2102,6 +2102,9 @@ typedef enum {
|
|||
this option is used only if PROXY_SSL_VERIFYPEER is true */
|
||||
CURLOPT(CURLOPT_PROXY_CAINFO_BLOB, CURLOPTTYPE_BLOB, 310),
|
||||
|
||||
/* used by scp/sftp to verify the host's public key */
|
||||
CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311),
|
||||
|
||||
CURLOPT_LASTENTRY /* the last unused */
|
||||
} CURLoption;
|
||||
|
||||
|
|
|
@ -317,6 +317,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
|
|||
(option) == CURLOPT_SERVICE_NAME || \
|
||||
(option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \
|
||||
(option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \
|
||||
(option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \
|
||||
(option) == CURLOPT_SSH_KNOWNHOSTS || \
|
||||
(option) == CURLOPT_SSH_PRIVATE_KEYFILE || \
|
||||
(option) == CURLOPT_SSH_PUBLIC_KEYFILE || \
|
||||
|
|
|
@ -271,6 +271,8 @@ struct curl_easyoption Curl_easyopts[] = {
|
|||
{"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0},
|
||||
{"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
|
||||
CURLOT_STRING, 0},
|
||||
{"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
|
||||
CURLOT_STRING, 0},
|
||||
{"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0},
|
||||
{"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0},
|
||||
{"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0},
|
||||
|
@ -354,6 +356,6 @@ struct curl_easyoption Curl_easyopts[] = {
|
|||
*/
|
||||
int Curl_easyopts_check(void)
|
||||
{
|
||||
return ((CURLOPT_LASTENTRY%10000) != (310 + 1));
|
||||
return ((CURLOPT_LASTENTRY%10000) != (311 + 1));
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -2477,6 +2477,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
va_arg(param, char *));
|
||||
break;
|
||||
|
||||
case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
|
||||
/*
|
||||
* Option to allow for the SHA256 of the host public key to be checked
|
||||
* for validation purposes.
|
||||
*/
|
||||
result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256],
|
||||
va_arg(param, char *));
|
||||
break;
|
||||
|
||||
case CURLOPT_SSH_KNOWNHOSTS:
|
||||
/*
|
||||
* Store the file name to read known hosts from.
|
||||
|
|
|
@ -1554,6 +1554,7 @@ enum dupstring {
|
|||
STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
|
||||
STRING_SSH_PUBLIC_KEY, /* path to the public key file for auth */
|
||||
STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
|
||||
STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */
|
||||
STRING_SSH_KNOWNHOSTS, /* file name of knownhosts file */
|
||||
STRING_PROXY_SERVICE_NAME, /* Proxy service name */
|
||||
STRING_SERVICE_NAME, /* Service name */
|
||||
|
|
|
@ -81,6 +81,11 @@
|
|||
#include "select.h"
|
||||
#include "warnless.h"
|
||||
#include "curl_path.h"
|
||||
#include "strcase.h"
|
||||
|
||||
#include <curl_base64.h> /* for base64 encoding/decoding */
|
||||
#include <curl_sha256.h>
|
||||
|
||||
|
||||
/* The last 3 #include files should be in this order */
|
||||
#include "curl_printf.h"
|
||||
|
@ -615,40 +620,142 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
|
|||
struct connectdata *conn = data->conn;
|
||||
struct ssh_conn *sshc = &conn->proto.sshc;
|
||||
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
|
||||
char md5buffer[33];
|
||||
const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
|
||||
|
||||
const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
|
||||
LIBSSH2_HOSTKEY_HASH_MD5);
|
||||
infof(data, "SSH MD5 public key: %s",
|
||||
pubkey_md5 != NULL ? pubkey_md5 : "NULL");
|
||||
infof(data, "SSH SHA256 public key: %s",
|
||||
pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
|
||||
|
||||
if(fingerprint) {
|
||||
if(pubkey_sha256) {
|
||||
const char *fingerprint = NULL;
|
||||
char *fingerprint_b64 = NULL;
|
||||
size_t fingerprint_b64_len;
|
||||
size_t pub_pos = 0;
|
||||
size_t b64_pos = 0;
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
/* The fingerprint points to static storage (!), don't free() it. */
|
||||
int i;
|
||||
for(i = 0; i < 16; i++)
|
||||
msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
|
||||
infof(data, "SSH MD5 fingerprint: %s", md5buffer);
|
||||
}
|
||||
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
|
||||
LIBSSH2_HOSTKEY_HASH_SHA256);
|
||||
#else
|
||||
const char *hostkey;
|
||||
size_t len = 0;
|
||||
unsigned char hash[32];
|
||||
|
||||
/* Before we authenticate we check the hostkey's MD5 fingerprint
|
||||
* against a known fingerprint, if available.
|
||||
*/
|
||||
if(pubkey_md5 && strlen(pubkey_md5) == 32) {
|
||||
if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
|
||||
if(fingerprint)
|
||||
failf(data,
|
||||
"Denied establishing ssh session: mismatch md5 fingerprint. "
|
||||
"Remote %s is not equal to %s", md5buffer, pubkey_md5);
|
||||
else
|
||||
failf(data,
|
||||
"Denied establishing ssh session: md5 fingerprint not available");
|
||||
hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL);
|
||||
if(hostkey) {
|
||||
Curl_sha256it(hash, (const unsigned char *) hostkey, len);
|
||||
fingerprint = (char *) hash;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!fingerprint) {
|
||||
failf(data,
|
||||
"Denied establishing ssh session: sha256 fingerprint "
|
||||
"not available");
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
infof(data, "MD5 checksum match!");
|
||||
|
||||
/* The length of fingerprint is 32 bytes for SHA256.
|
||||
* See libssh2_hostkey_hash documentation. */
|
||||
if(Curl_base64_encode (data, fingerprint, 32, &fingerprint_b64,
|
||||
&fingerprint_b64_len) != CURLE_OK) {
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
|
||||
if(!fingerprint_b64) {
|
||||
failf(data,
|
||||
"sha256 fingerprint could not be encoded");
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
|
||||
infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
|
||||
|
||||
/* Find the position of any = padding characters in the public key */
|
||||
while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) {
|
||||
pub_pos++;
|
||||
}
|
||||
|
||||
/* Find the position of any = padding characters in the base64 coded
|
||||
* hostkey fingerprint */
|
||||
while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) {
|
||||
b64_pos++;
|
||||
}
|
||||
|
||||
/* Before we authenticate we check the hostkey's sha256 fingerprint
|
||||
* against a known fingerprint, if available.
|
||||
*/
|
||||
if((pub_pos != b64_pos) ||
|
||||
Curl_strncasecompare(fingerprint_b64, pubkey_sha256, pub_pos) != 1) {
|
||||
free(fingerprint_b64);
|
||||
|
||||
failf(data,
|
||||
"Denied establishing ssh session: mismatch sha256 fingerprint. "
|
||||
"Remote %s is not equal to %s", fingerprint, pubkey_sha256);
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
|
||||
free(fingerprint_b64);
|
||||
|
||||
infof(data, "SHA256 checksum match!");
|
||||
}
|
||||
|
||||
if(pubkey_md5) {
|
||||
char md5buffer[33];
|
||||
const char *fingerprint = NULL;
|
||||
|
||||
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
|
||||
LIBSSH2_HOSTKEY_HASH_MD5);
|
||||
|
||||
if(fingerprint) {
|
||||
/* The fingerprint points to static storage (!), don't free() it. */
|
||||
int i;
|
||||
for(i = 0; i < 16; i++) {
|
||||
msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
|
||||
}
|
||||
|
||||
infof(data, "SSH MD5 fingerprint: %s", md5buffer);
|
||||
}
|
||||
|
||||
/* Before we authenticate we check the hostkey's MD5 fingerprint
|
||||
* against a known fingerprint, if available.
|
||||
*/
|
||||
if(pubkey_md5 && strlen(pubkey_md5) == 32) {
|
||||
if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
|
||||
if(fingerprint) {
|
||||
failf(data,
|
||||
"Denied establishing ssh session: mismatch md5 fingerprint. "
|
||||
"Remote %s is not equal to %s", md5buffer, pubkey_md5);
|
||||
}
|
||||
else {
|
||||
failf(data,
|
||||
"Denied establishing ssh session: md5 fingerprint "
|
||||
"not available");
|
||||
}
|
||||
state(data, SSH_SESSION_FREE);
|
||||
sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
|
||||
return sshc->actualcode;
|
||||
}
|
||||
infof(data, "MD5 checksum match!");
|
||||
}
|
||||
}
|
||||
|
||||
if(!pubkey_md5 && !pubkey_sha256) {
|
||||
return ssh_knownhost(data);
|
||||
}
|
||||
else {
|
||||
/* as we already matched, we skip the check for known hosts */
|
||||
return CURLE_OK;
|
||||
}
|
||||
return ssh_knownhost(data);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -131,6 +131,7 @@ static void free_config_fields(struct OperationConfig *config)
|
|||
Curl_safefree(config->proxy_key_passwd);
|
||||
Curl_safefree(config->pubkey);
|
||||
Curl_safefree(config->hostpubmd5);
|
||||
Curl_safefree(config->hostpubsha256);
|
||||
Curl_safefree(config->engine);
|
||||
Curl_safefree(config->etag_save_file);
|
||||
Curl_safefree(config->etag_compare_file);
|
||||
|
|
|
@ -158,6 +158,7 @@ struct OperationConfig {
|
|||
char *proxy_key_passwd;
|
||||
char *pubkey;
|
||||
char *hostpubmd5;
|
||||
char *hostpubsha256;
|
||||
char *engine;
|
||||
char *etag_save_file;
|
||||
char *etag_compare_file;
|
||||
|
|
|
@ -241,6 +241,7 @@ static const struct LongShort aliases[]= {
|
|||
{"Eg", "capath", ARG_FILENAME},
|
||||
{"Eh", "pubkey", ARG_STRING},
|
||||
{"Ei", "hostpubmd5", ARG_STRING},
|
||||
{"EF", "hostpubsha256", ARG_STRING},
|
||||
{"Ej", "crlfile", ARG_FILENAME},
|
||||
{"Ek", "tlsuser", ARG_STRING},
|
||||
{"El", "tlspassword", ARG_STRING},
|
||||
|
@ -1602,6 +1603,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
|||
if(!config->hostpubmd5 || strlen(config->hostpubmd5) != 32)
|
||||
return PARAM_BAD_USE;
|
||||
break;
|
||||
case 'F': /* --hostpubsha256 sha256 of the host public key */
|
||||
GetStr(&config->hostpubsha256, nextarg);
|
||||
break;
|
||||
case 'j': /* CRL file */
|
||||
GetStr(&config->crlfile, nextarg);
|
||||
break;
|
||||
|
|
|
@ -346,6 +346,9 @@ static const struct helptxt helptext[] = {
|
|||
{" --hostpubmd5 <md5>",
|
||||
"Acceptable MD5 hash of the host public key",
|
||||
CURLHELP_SFTP | CURLHELP_SCP},
|
||||
{" --hostpubsha256 <sha256>",
|
||||
"Acceptable SHA256 hash of the host public key",
|
||||
CURLHELP_SFTP | CURLHELP_SCP},
|
||||
{" --hsts <file name>",
|
||||
"Enable HSTS with this cache file",
|
||||
CURLHELP_HTTP},
|
||||
|
|
|
@ -1408,6 +1408,11 @@ static CURLcode single_transfer(struct GlobalConfig *global,
|
|||
my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
|
||||
config->hostpubmd5);
|
||||
|
||||
/* new in libcurl 7.80.0: SSH host key sha256 checking allows us
|
||||
to fail if we are not talking to who we think we should */
|
||||
my_setopt_str(curl, CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
|
||||
config->hostpubsha256);
|
||||
|
||||
/* new in libcurl 7.56.0 */
|
||||
if(config->ssh_compression)
|
||||
my_setopt(curl, CURLOPT_SSH_COMPRESSION, 1L);
|
||||
|
|
|
@ -7,6 +7,7 @@ curl_client_knownhosts
|
|||
curl_host_rsa_key
|
||||
curl_host_rsa_key.pub
|
||||
curl_host_rsa_key.pub_md5
|
||||
curl_host_rsa_key.pub_sha256
|
||||
curl_sftp_cmds
|
||||
curl_sftp_config
|
||||
curl_ssh_config
|
||||
|
|
|
@ -148,6 +148,7 @@ Available substitute variables include:
|
|||
- `%SRCDIR` - Full path to the source dir
|
||||
- `%SSHPORT` - Port number of the SCP/SFTP server
|
||||
- `%SSHSRVMD5` - MD5 of SSH server's public key
|
||||
- `%SSHSRVSHA256` - SHA256 of SSH server's public key
|
||||
- `%SSH_PWD` - Current directory friendly for the SSH server
|
||||
- `%TESTNUMBER` - Number of the test case
|
||||
- `%TFTP6PORT` - IPv6 port number of the TFTP server
|
||||
|
|
|
@ -237,4 +237,4 @@ test2200 test2201 test2202 test2203 test2204 test2205 \
|
|||
\
|
||||
test3000 test3001 test3002 test3003 test3004 test3005 test3006 test3007 \
|
||||
test3008 test3009 test3010 test3011 test3012 test3013 test3014 test3015 \
|
||||
test3016 test3017 test3018 test3019 test3020
|
||||
test3016 test3017 test3018 test3019 test3020 test3021 test3022
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
SFTP
|
||||
server sha256 key check
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
test
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
sftp
|
||||
</server>
|
||||
<name>
|
||||
SFTP correct sha256 host key
|
||||
</name>
|
||||
<command>
|
||||
--hostpubsha256 %SSHSRVSHA256 --key curl_client_key --pubkey curl_client_key.pub -u %USER: sftp://%HOSTIP:%SSHPORT%SSH_PWD/log/file%TESTNUMBER.txt
|
||||
</command>
|
||||
<file name="log/file%TESTNUMBER.txt">
|
||||
test
|
||||
</file>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<errorcode>
|
||||
0
|
||||
</errorcode>
|
||||
<valgrind>
|
||||
disable
|
||||
</valgrind>
|
||||
</verify>
|
||||
</testcase>
|
|
@ -0,0 +1,44 @@
|
|||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
SCP
|
||||
server sha256 key check
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
test
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
scp
|
||||
</server>
|
||||
<name>
|
||||
SCP correct sha256 host key
|
||||
</name>
|
||||
<command>
|
||||
--hostpubsha256 %SSHSRVSHA256 --key curl_client_key --pubkey curl_client_key.pub -u %USER: scp://%HOSTIP:%SSHPORT%SSH_PWD/log/file%TESTNUMBER.txt
|
||||
</command>
|
||||
<file name="log/file%TESTNUMBER.txt">
|
||||
test
|
||||
</file>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<errorcode>
|
||||
0
|
||||
</errorcode>
|
||||
<valgrind>
|
||||
disable
|
||||
</valgrind>
|
||||
</verify>
|
||||
</testcase>
|
|
@ -168,6 +168,7 @@ my $proxy_address;
|
|||
my %custom_skip_reasons;
|
||||
|
||||
my $SSHSRVMD5 = "[uninitialized]"; # MD5 of ssh server public key
|
||||
my $SSHSRVSHA256 = "[uninitialized]"; # SHA256 of ssh server public key
|
||||
my $VERSION=""; # curl's reported version number
|
||||
|
||||
my $srcdir = $ENV{'srcdir'} || '.';
|
||||
|
@ -2287,6 +2288,17 @@ sub runsshserver {
|
|||
die $msg;
|
||||
}
|
||||
|
||||
my $hstpubsha256f = "curl_host_rsa_key.pub_sha256";
|
||||
if(!open(PUBSHA256FILE, "<", $hstpubsha256f) ||
|
||||
(read(PUBSHA256FILE, $SSHSRVSHA256, 48) == 0) ||
|
||||
!close(PUBSHA256FILE))
|
||||
{
|
||||
my $msg = "Fatal: $srvrname pubkey sha256 missing : \"$hstpubsha256f\" : $!";
|
||||
logmsg "$msg\n";
|
||||
stopservers($verbose);
|
||||
die $msg;
|
||||
}
|
||||
|
||||
logmsg "RUN: $srvrname on PID $pid2 port $wport\n" if($verbose);
|
||||
|
||||
return ($pid2, $sshpid, $wport);
|
||||
|
@ -3374,6 +3386,7 @@ sub subVariables {
|
|||
$$thing =~ s/${prefix}USER/$USER/g;
|
||||
|
||||
$$thing =~ s/${prefix}SSHSRVMD5/$SSHSRVMD5/g;
|
||||
$$thing =~ s/${prefix}SSHSRVSHA256/$SSHSRVSHA256/g;
|
||||
|
||||
# The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
|
||||
# used for time-out tests and that would work on most hosts as these
|
||||
|
|
|
@ -51,6 +51,7 @@ use vars qw(
|
|||
$hstprvkeyf
|
||||
$hstpubkeyf
|
||||
$hstpubmd5f
|
||||
$hstpubsha256f
|
||||
$cliprvkeyf
|
||||
$clipubkeyf
|
||||
@sftppath
|
||||
|
@ -84,6 +85,7 @@ use vars qw(
|
|||
$hstprvkeyf
|
||||
$hstpubkeyf
|
||||
$hstpubmd5f
|
||||
$hstpubsha256f
|
||||
$cliprvkeyf
|
||||
$clipubkeyf
|
||||
display_sshdconfig
|
||||
|
@ -125,6 +127,7 @@ $knownhosts = 'curl_client_knownhosts'; # ssh knownhosts file
|
|||
$hstprvkeyf = 'curl_host_rsa_key'; # host private key file
|
||||
$hstpubkeyf = 'curl_host_rsa_key.pub'; # host public key file
|
||||
$hstpubmd5f = 'curl_host_rsa_key.pub_md5'; # md5 hash of host public key
|
||||
$hstpubsha256f = 'curl_host_rsa_key.pub_sha256'; # sha256 hash of host public key
|
||||
$cliprvkeyf = 'curl_client_key'; # client private key file
|
||||
$clipubkeyf = 'curl_client_key.pub'; # client public key file
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ use Cwd;
|
|||
use Cwd 'abs_path';
|
||||
use Digest::MD5;
|
||||
use Digest::MD5 'md5_hex';
|
||||
use Digest::SHA;
|
||||
use Digest::SHA 'sha256_base64';
|
||||
use MIME::Base64;
|
||||
|
||||
#***************************************************************************
|
||||
|
@ -52,6 +54,7 @@ use sshhelp qw(
|
|||
$hstprvkeyf
|
||||
$hstpubkeyf
|
||||
$hstpubmd5f
|
||||
$hstpubsha256f
|
||||
$cliprvkeyf
|
||||
$clipubkeyf
|
||||
display_sshdconfig
|
||||
|
@ -362,10 +365,12 @@ if((($sshid =~ /OpenSSH/) && ($sshvernum < 299)) ||
|
|||
if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) ||
|
||||
(! -e $hstpubkeyf) || (! -s $hstpubkeyf) ||
|
||||
(! -e $hstpubmd5f) || (! -s $hstpubmd5f) ||
|
||||
(! -e $hstpubsha256f) || (! -s $hstpubsha256f) ||
|
||||
(! -e $cliprvkeyf) || (! -s $cliprvkeyf) ||
|
||||
(! -e $clipubkeyf) || (! -s $clipubkeyf)) {
|
||||
# Make sure all files are gone so ssh-keygen doesn't complain
|
||||
unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $cliprvkeyf, $clipubkeyf);
|
||||
unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $hstpubsha256f,
|
||||
$cliprvkeyf, $clipubkeyf);
|
||||
logmsg 'generating host keys...' if($verbose);
|
||||
if(system "\"$sshkeygen\" -q -t rsa -f $hstprvkeyf -C 'curl test server' -N ''") {
|
||||
logmsg 'Could not generate host key';
|
||||
|
@ -379,7 +384,7 @@ if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) ||
|
|||
# Make sure that permissions are restricted so openssh doesn't complain
|
||||
system "chmod 600 $hstprvkeyf";
|
||||
system "chmod 600 $cliprvkeyf";
|
||||
# Save md5 hash of public host key
|
||||
# Save md5 and sha256 hashes of public host key
|
||||
open(RSAKEYFILE, "<$hstpubkeyf");
|
||||
my @rsahostkey = do { local $/ = ' '; <RSAKEYFILE> };
|
||||
close(RSAKEYFILE);
|
||||
|
@ -394,6 +399,13 @@ if((! -e $hstprvkeyf) || (! -s $hstprvkeyf) ||
|
|||
logmsg 'Failed writing md5 hash of RSA host key';
|
||||
exit 1;
|
||||
}
|
||||
open(PUBSHA256FILE, ">$hstpubsha256f");
|
||||
print PUBSHA256FILE sha256_base64(decode_base64($rsahostkey[1]));
|
||||
close(PUBSHA256FILE);
|
||||
if((! -e $hstpubsha256f) || (! -s $hstpubsha256f)) {
|
||||
logmsg 'Failed writing sha256 hash of RSA host key';
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1141,7 +1153,7 @@ elsif($verbose && ($rc >> 8)) {
|
|||
#***************************************************************************
|
||||
# Clean up once the server has stopped
|
||||
#
|
||||
unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f,
|
||||
unlink($hstprvkeyf, $hstpubkeyf, $hstpubmd5f, $hstpubsha256f,
|
||||
$cliprvkeyf, $clipubkeyf, $knownhosts,
|
||||
$sshdconfig, $sshconfig, $sftpconfig);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче