Skip to content

Commit

Permalink
Issue proftpd#1457: Implement an SFTPHostKeys directive for tuning …
Browse files Browse the repository at this point in the history
…the SSH host key algorithms used during KEX.
  • Loading branch information
Castaglia committed May 14, 2022
1 parent 08719a4 commit b307fba
Show file tree
Hide file tree
Showing 9 changed files with 489 additions and 71 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
- Issue 1448 - Ensure that mod_sftp algorithms work properly with OpenSSL 3.x.
- Issue 1445 - BanOnEvent BadProtocol triggers segfault.
- Issue 1439 - SFTP "check-file" implementation computes incorrect results.
- Issue 1457 - Implement SFTPHostKeys directive for configuring the SSH host
key algorithms.

1.3.8rc3 - Released 23-Apr-2022
--------------------------------
Expand Down
4 changes: 4 additions & 0 deletions RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ ChangeLog files.

+ Ensure that mod_sftp algorithms work properly when OpenSSL 3.x is used.

+ New Directives

SFTPHostKeys (Issue #1457)

+ Changed Directives

SFTPClientMatch (Issue #1444)
Expand Down
40 changes: 40 additions & 0 deletions contrib/mod_sftp/crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,26 @@ static struct sftp_digest digests[] = {
{ NULL, NULL, NULL, 0, FALSE, FALSE }
};

static const char *hostkey_algos[] = {
#if defined(PR_USE_SODIUM)
"ssh-ed25519",
#endif /* PR_USE_SODIUM */
#if defined(PR_USE_OPENSSL_ECC)
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp521",
#endif /* PR_USE_OPENSSL_ECC */
#if defined(HAVE_SHA512_OPENSSL)
"rsa-sha2-512",
#endif /* HAVE_SHA512_OPENSSL */
#if defined(HAVE_SHA256_OPENSSL)
"rsa-sha2-256",
#endif /* HAVE_SHA256_OPENSSL */
"ssh-rsa",
"ssh-dss",
NULL
};

static const char *key_exchanges[] = {
"diffie-hellman-group1-sha1",
"diffie-hellman-group14-sha1",
Expand Down Expand Up @@ -1163,6 +1183,26 @@ const EVP_MD *sftp_crypto_get_digest(const char *name, uint32_t *mac_len) {
return NULL;
}

int sftp_crypto_is_hostkey(const char *name) {
register unsigned int i;

if (name == NULL) {
errno = EINVAL;
return -1;
}

for (i = 0; hostkey_algos[i]; i++) {
if (strcmp(hostkey_algos[i], name) == 0) {
return TRUE;
}
}

(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
"no hostkey matching '%s' found", name);
errno = ENOENT;
return -1;
}

int sftp_crypto_is_key_exchange(const char *name) {
register unsigned int i;

Expand Down
1 change: 1 addition & 0 deletions contrib/mod_sftp/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void sftp_crypto_free(int);
const EVP_CIPHER *sftp_crypto_get_cipher(const char *name, size_t *key_len,
size_t *auth_len, size_t *discard_len);
const EVP_MD *sftp_crypto_get_digest(const char *, uint32_t *);
int sftp_crypto_is_hostkey(const char *name);
int sftp_crypto_is_key_exchange(const char *name);
int sftp_crypto_set_driver(const char *);
const char *sftp_crypto_get_kexinit_cipher_list(pool *);
Expand Down
14 changes: 14 additions & 0 deletions contrib/mod_sftp/interop.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ int sftp_interop_handle_version(pool *p, const char *client_version) {
* pessimisticNewkeys
* sftpCiphers
* sftpDigests
* sftpHostKeys
* sftpKeyExchanges
* sftpMinProtocolVersion
* sftpMaxProtocolVersion
Expand Down Expand Up @@ -379,6 +380,19 @@ int sftp_interop_handle_version(pool *p, const char *client_version) {
pr_config_add_config_to_set(main_server->conf, digests, 0);
}

v = pr_table_get(tab, "sftpHostKeys", NULL);
if (v != NULL) {
config_rec *hostkeys;

hostkeys = *((config_rec **) v);

pr_trace_msg(trace_channel, 16,
"setting new SSH host key algorithms, per SFTPClientMatch");

remove_config(main_server->conf, "SFTPHostKeys", FALSE);
pr_config_add_config_to_set(main_server->conf, hostkeys, 0);
}

v = pr_table_get(tab, "sftpKeyExchanges", NULL);
if (v != NULL) {
config_rec *key_exchanges;
Expand Down
213 changes: 146 additions & 67 deletions contrib/mod_sftp/kex.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,71 +1596,148 @@ static const char *get_kexinit_exchange_list(pool *p) {
}

static const char *get_kexinit_hostkey_algo_list(pool *p) {
#ifdef PR_USE_OPENSSL_ECC
int *nids = NULL, res;
#endif /* PR_USE_OPENSSL_ECC */
register unsigned int i;
config_rec *c;
array_header *hostkey_algos;
char *list = "";

/* Our list of supported hostkey algorithms depends on the hostkeys
* that have been configured. Show a preference for RSA over DSA,
* and ECDSA over both RSA and DSA, and ED25519 over all.
*
* XXX Should this be configurable later?
*/
hostkey_algos = make_array(p, 1, sizeof(char *));

if (sftp_keys_have_ed25519_hostkey() == 0) {
list = pstrcat(p, list, *list ? "," : "", "ssh-ed25519", NULL);
}
c = find_config(main_server->conf, CONF_PARAM, "SFTPHostKeys", FALSE);
if (c != NULL) {
for (i = 0; i < c->argc; i++) {
*((char **) push_array(hostkey_algos)) = pstrdup(p, c->argv[i]);
}

#ifdef PR_USE_OPENSSL_ECC
res = sftp_keys_have_ecdsa_hostkey(p, &nids);
if (res > 0) {
register int i;
} else {
/* Create our default list of host key algorithms, in preference order. */
*((char **) push_array(hostkey_algos)) = pstrdup(p, "ssh-ed25519");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "ecdsa-sha2-nistp256");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "ecdsa-sha2-nistp384");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "ecdsa-sha2-nistp521");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "rsa-sha2-512");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "rsa-sha2-256");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "ssh-rsa");
*((char **) push_array(hostkey_algos)) = pstrdup(p, "ssh-dss");
}

for (i = 0; i < hostkey_algos->nelts; i++) {
const char *algo;
int have_key = FALSE, supported_algo = FALSE;

for (i = 0; i < res; i++) {
char *algo_name = NULL;
algo = ((char **) hostkey_algos->elts)[i];

switch (nids[i]) {
case NID_X9_62_prime256v1:
algo_name = "ecdsa-sha2-nistp256";
break;
if (strcmp(algo, "ssh-ed25519") == 0) {
#if defined(PR_USE_SODIUM)
supported_algo = TRUE;
#endif /* PR_USE_SODIUM */

case NID_secp384r1:
algo_name = "ecdsa-sha2-nistp384";
break;
if (sftp_keys_have_ed25519_hostkey() == 0) {
have_key = TRUE;
}

case NID_secp521r1:
algo_name = "ecdsa-sha2-nistp521";
break;
#if defined(PR_USE_OPENSSL_ECC)
} else if (strcmp(algo, "ecdsa-sha2-nistp256") == 0) {
int *nids = NULL, res;

supported_algo = TRUE;
res = sftp_keys_have_ecdsa_hostkey(p, &nids);
if (res > 0) {
register int j;

for (j = 0; j < res; i++) {
if (nids[j] == NID_X9_62_prime256v1) {
have_key = TRUE;
break;
}
}
}

default:
(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
"unknown/unsupported ECDSA NID %d, skipping", nids[i]);
break;
} else if (strcmp(algo, "ecdsa-sha2-nistp384") == 0) {
int *nids = NULL, res;

supported_algo = TRUE;
res = sftp_keys_have_ecdsa_hostkey(p, &nids);
if (res > 0) {
register int j;

for (j = 0; j < res; i++) {
if (nids[j] == NID_secp384r1) {
have_key = TRUE;
break;
}
}
}

if (algo_name != NULL) {
list = pstrcat(p, list, *list ? "," : "", algo_name, NULL);
} else if (strcmp(algo, "ecdsa-sha2-nistp521") == 0) {
int *nids = NULL, res;

supported_algo = TRUE;
res = sftp_keys_have_ecdsa_hostkey(p, &nids);
if (res > 0) {
register int j;

for (j = 0; j < res; i++) {
if (nids[j] == NID_secp521r1) {
have_key = TRUE;
break;
}
}
}
}
}
#endif /* PR_USE_OPENSSL_ECC */

if (sftp_keys_have_rsa_hostkey() == 0) {
#if defined(HAVE_SHA512_OPENSSL)
list = pstrcat(p, list, *list ? "," : "", "rsa-sha2-512", NULL);
} else if (strcmp(algo, "rsa-sha2-512") == 0) {
supported_algo = TRUE;

if (sftp_keys_have_rsa_hostkey() == 0) {
have_key = TRUE;
}
#endif /* HAVE_SHA512_OPENSSL */

#if defined(HAVE_SHA256_OPENSSL)
list = pstrcat(p, list, *list ? "," : "", "rsa-sha2-256", NULL);
} else if (strcmp(algo, "rsa-sha2-256") == 0) {
supported_algo = TRUE;

if (sftp_keys_have_rsa_hostkey() == 0) {
have_key = TRUE;
}
#endif /* HAVE_SHA256_OPENSSL */

list = pstrcat(p, list, *list ? "," : "", "ssh-rsa", NULL);
}
} else if (strcmp(algo, "ssh-rsa") == 0) {
supported_algo = TRUE;

if (sftp_keys_have_dsa_hostkey() == 0) {
list = pstrcat(p, list, *list ? "," : "", "ssh-dss", NULL);
}
if (sftp_keys_have_rsa_hostkey() == 0) {
have_key = TRUE;
}

} else if (strcmp(algo, "ssh-dss") == 0) {
supported_algo = TRUE;

if (sftp_keys_have_dsa_hostkey() == 0) {
have_key = TRUE;
}
}

if (supported_algo == TRUE &&
have_key == TRUE) {
list = pstrcat(p, list, *list ? "," : "", algo, NULL);

} else {
if (supported_algo == FALSE) {
pr_trace_msg(trace_channel, 19,
"omitting host key algorithm '%s' due to lack of support", algo);

} else {
pr_trace_msg(trace_channel, 19,
"omitting host key algorithm '%s' due to lack of key", algo);
}
}
}

return list;
}
Expand Down Expand Up @@ -2009,62 +2086,64 @@ static int setup_kex_algo(struct sftp_kex *kex, const char *algo) {
}

static int setup_hostkey_algo(struct sftp_kex *kex, const char *algo) {
int res = -1;

kex->session_names->server_hostkey_algo = (char *) algo;

if (strcmp(algo, "ssh-dss") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_DSA;
return 0;
}
res = 0;

if (strcmp(algo, "ssh-rsa") == 0) {
} else if (strcmp(algo, "ssh-rsa") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_RSA;
return 0;
}
res = 0;

#if defined(HAVE_SHA256_OPENSSL)
if (strcmp(algo, "rsa-sha2-256") == 0) {
} else if (strcmp(algo, "rsa-sha2-256") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_RSA_SHA256;
return 0;
}
res = 0;
#endif /* HAVE_SHA256_OPENSSL */

#if defined(HAVE_SHA512_OPENSSL)
if (strcmp(algo, "rsa-sha2-512") == 0) {
} else if (strcmp(algo, "rsa-sha2-512") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_RSA_SHA512;
return 0;
}
res = 0;
#endif /* HAVE_SHA512_OPENSSL */

#ifdef PR_USE_OPENSSL_ECC
if (strcmp(algo, "ecdsa-sha2-nistp256") == 0) {
#if defined(PR_USE_OPENSSL_ECC)
} else if (strcmp(algo, "ecdsa-sha2-nistp256") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_ECDSA_256;
return 0;
}
res = 0;

if (strcmp(algo, "ecdsa-sha2-nistp384") == 0) {
} else if (strcmp(algo, "ecdsa-sha2-nistp384") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_ECDSA_384;
return 0;
}
res = 0;

if (strcmp(algo, "ecdsa-sha2-nistp521") == 0) {
} else if (strcmp(algo, "ecdsa-sha2-nistp521") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_ECDSA_521;
return 0;
}
res = 0;
#endif /* PR_USE_OPENSSL_ECC */

#ifdef PR_USE_SODIUM
if (strcmp(algo, "ssh-ed25519") == 0) {
#if defined(PR_USE_SODIUM)
} else if (strcmp(algo, "ssh-ed25519") == 0) {
kex->use_hostkey_type = kex_used_hostkey_type = SFTP_KEY_ED25519;
return 0;
}
res = 0;
#endif /* PR_USE_SODIUM */
}

/* XXX Need to handle "x509v3-ssh-dss", "x509v3-ssh-rsa", "x509v3-sign"
* algorithms here.
*/

errno = EINVAL;
return -1;
if (res < 0) {
errno = EINVAL;
return -1;
}

(void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION,
" + Session host key algorithm: %s", algo);
pr_trace_msg(trace_channel, 20, "session host key algorithm: %s", algo);
return 0;
}

static int setup_c2s_encrypt_algo(struct sftp_kex *kex, const char *algo) {
Expand Down
Loading

0 comments on commit b307fba

Please sign in to comment.