Compare commits

...

9 Commits

Author SHA1 Message Date
Mike Frysinger
3526e02dee use standard O_NONBLOCK naming
Systems define O_NONBLOCK & O_NDELAY as the same thing.  POSIX however
only defines O_NONBLOCK.  Rename the current define to be portable.

Signed-off-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-12 18:18:02 +01:00
abdallah elhdad
ecea5b6052 Support new '-o' option parsing to client
Signed-off-by: abdallah elhdad <abdallahselhdad@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-12 18:15:42 +01:00
abdallah elhdad
1833ce86f9 refactor auth options handler
Signed-off-by: abdallah elhdad <abdallahselhdad@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-12 18:15:41 +01:00
abdallah elhdad
3938e5e850 set log level when debug option is increased
Signed-off-by: abdallah elhdad <abdallahselhdad@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-12 18:15:40 +01:00
Norbert Pocs
dd80a56029 libcrypto.c: Use openssl const algorithm names
Use the openssl constants algorithm names instead of string
representations. They should not change, but it's clearer to have it
this way.

Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Signed-off-by: Norbert Pocs <norbertp@openssl.org>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-12 18:12:13 +01:00
Jakub Jelen
9d6df9d0fa ssh_known_hosts_get_algorithms: Simplify cleanup ...
...  and prevent memory leak of host_port on memory allocation failure.

Thanks Xiaoke Wang for the report!

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-12-12 18:06:47 +01:00
Jakub Jelen
ee180c660e server: Check strdup allocation failure
Thanks Xiaoke Wang for the report!

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-12-12 18:06:45 +01:00
abdallah elhdad
541cd39f14 zeroize sensitive buffers in ssh_sntrup761x25519_build_k
Signed-off-by: abdallah elhdad <abdallahselhdad@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-12-12 18:03:21 +01:00
abdallah elhdad
64f72ed55f Replace explicit_bzero with ssh_burn
Signed-off-by: abdallah elhdad <abdallahselhdad@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-12-12 18:03:19 +01:00
34 changed files with 634 additions and 344 deletions

View File

@@ -139,6 +139,7 @@ check_function_exists(strncpy HAVE_STRNCPY)
check_function_exists(strndup HAVE_STRNDUP) check_function_exists(strndup HAVE_STRNDUP)
check_function_exists(strtoull HAVE_STRTOULL) check_function_exists(strtoull HAVE_STRTOULL)
check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
check_function_exists(memset_explicit HAVE_MEMSET_EXPLICIT)
check_function_exists(memset_s HAVE_MEMSET_S) check_function_exists(memset_s HAVE_MEMSET_S)
if (HAVE_GLOB_H) if (HAVE_GLOB_H)

View File

@@ -179,6 +179,9 @@
/* Define to 1 if you have the `explicit_bzero' function. */ /* Define to 1 if you have the `explicit_bzero' function. */
#cmakedefine HAVE_EXPLICIT_BZERO 1 #cmakedefine HAVE_EXPLICIT_BZERO 1
/* Define to 1 if you have the `memset_explicit' function. */
#cmakedefine HAVE_MEMSET_EXPLICIT 1
/* Define to 1 if you have the `memset_s' function. */ /* Define to 1 if you have the `memset_s' function. */
#cmakedefine HAVE_MEMSET_S 1 #cmakedefine HAVE_MEMSET_S 1

View File

@@ -86,22 +86,24 @@ static void add_cmd(char *cmd)
static void usage(void) static void usage(void)
{ {
fprintf(stderr, fprintf(
"Usage : ssh [options] [login@]hostname\n" stderr,
"sample client - libssh-%s\n" "Usage : ssh [options] [login@]hostname\n"
"Options :\n" "sample client - libssh-%s\n"
" -l user : log in as user\n" "Options :\n"
" -p port : connect to port\n" " -l user : log in as user\n"
" -r : use RSA to verify host public key\n" " -p port : connect to port\n"
" -F file : parse configuration file instead of default one\n" " -o option : set configuration option (e.g., -o Compression=yes)\n"
" -r : use RSA to verify host public key\n"
" -F file : parse configuration file instead of default one\n"
#ifdef WITH_PCAP #ifdef WITH_PCAP
" -P file : create a pcap debugging file\n" " -P file : create a pcap debugging file\n"
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
" -T proxycommand : command to execute as a socket proxy\n" " -T proxycommand : command to execute as a socket proxy\n"
#endif #endif
"\n", "\n",
ssh_version(0)); ssh_version(0));
exit(0); exit(0);
} }

View File

@@ -24,6 +24,7 @@
#ifndef LIBSSH_CONFIG_H_ #ifndef LIBSSH_CONFIG_H_
#define LIBSSH_CONFIG_H_ #define LIBSSH_CONFIG_H_
#include "libssh/libssh.h"
enum ssh_config_opcode_e { enum ssh_config_opcode_e {
/* Unknown opcode */ /* Unknown opcode */
@@ -70,4 +71,7 @@ enum ssh_config_opcode_e {
SOC_MAX /* Keep this one last in the list */ SOC_MAX /* Keep this one last in the list */
}; };
enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword);
int ssh_config_parse_line_cli(ssh_session session, const char *line);
#endif /* LIBSSH_CONFIG_H_ */ #endif /* LIBSSH_CONFIG_H_ */

View File

@@ -365,9 +365,29 @@ int ssh_connector_remove_event(ssh_connector connector);
/** Get the size of an array */ /** Get the size of an array */
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
#ifndef HAVE_EXPLICIT_BZERO /** Securely zero memory in a way that won't be optimized away */
void explicit_bzero(void *s, size_t n); #if defined(HAVE_MEMSET_EXPLICIT)
#endif /* !HAVE_EXPLICIT_BZERO */ #define ssh_burn(ptr, len) memset_explicit((ptr), '\0', (len))
#elif defined(HAVE_EXPLICIT_BZERO)
#define ssh_burn(ptr, len) explicit_bzero((ptr), (len))
#elif defined(HAVE_MEMSET_S)
#define ssh_burn(ptr, len) memset_s((ptr), (len), '\0', (len))
#elif defined(HAVE_SECURE_ZERO_MEMORY)
#define ssh_burn(ptr, len) SecureZeroMemory((ptr), (len))
#else
#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION)
#define ssh_burn(ptr, len) \
do { \
memset((ptr), '\0', (len)); \
__asm__ volatile("" : : "g"(ptr) : "memory"); \
} while (0)
#else
#define ssh_burn(ptr, len) \
do { \
memset((ptr), '\0', (len)); \
} while (0)
#endif
#endif
void burn_free(void *ptr, size_t len); void burn_free(void *ptr, size_t len);

View File

@@ -1845,7 +1845,7 @@ void ssh_kbdint_free(ssh_kbdint kbd)
if (kbd->prompts) { if (kbd->prompts) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (kbd->prompts[i] != NULL) { if (kbd->prompts[i] != NULL) {
explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i])); ssh_burn(kbd->prompts[i], strlen(kbd->prompts[i]));
} }
SAFE_FREE(kbd->prompts[i]); SAFE_FREE(kbd->prompts[i]);
} }
@@ -1856,7 +1856,7 @@ void ssh_kbdint_free(ssh_kbdint kbd)
if (kbd->answers) { if (kbd->answers) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
if (kbd->answers[i] != NULL) { if (kbd->answers[i] != NULL) {
explicit_bzero(kbd->answers[i], strlen(kbd->answers[i])); ssh_burn(kbd->answers[i], strlen(kbd->answers[i]));
} }
SAFE_FREE(kbd->answers[i]); SAFE_FREE(kbd->answers[i]);
} }
@@ -1881,7 +1881,7 @@ void ssh_kbdint_clean(ssh_kbdint kbd)
n = kbd->nprompts; n = kbd->nprompts;
if (kbd->prompts) { if (kbd->prompts) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
explicit_bzero(kbd->prompts[i], strlen(kbd->prompts[i])); ssh_burn(kbd->prompts[i], strlen(kbd->prompts[i]));
SAFE_FREE(kbd->prompts[i]); SAFE_FREE(kbd->prompts[i]);
} }
SAFE_FREE(kbd->prompts); SAFE_FREE(kbd->prompts);
@@ -1891,7 +1891,7 @@ void ssh_kbdint_clean(ssh_kbdint kbd)
if (kbd->answers) { if (kbd->answers) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
explicit_bzero(kbd->answers[i], strlen(kbd->answers[i])); ssh_burn(kbd->answers[i], strlen(kbd->answers[i]));
SAFE_FREE(kbd->answers[i]); SAFE_FREE(kbd->answers[i]);
} }
SAFE_FREE(kbd->answers); SAFE_FREE(kbd->answers);
@@ -2372,8 +2372,8 @@ ssh_userauth_kbdint_setanswer(ssh_session session, unsigned int i,
} }
if (session->kbdint->answers[i]) { if (session->kbdint->answers[i]) {
explicit_bzero(session->kbdint->answers[i], ssh_burn(session->kbdint->answers[i],
strlen(session->kbdint->answers[i])); strlen(session->kbdint->answers[i]));
SAFE_FREE(session->kbdint->answers[i]); SAFE_FREE(session->kbdint->answers[i]);
} }

View File

@@ -156,10 +156,10 @@ void ssh_buffer_free(struct ssh_buffer_struct *buffer)
if (buffer->secure && buffer->allocated > 0) { if (buffer->secure && buffer->allocated > 0) {
/* burn the data */ /* burn the data */
explicit_bzero(buffer->data, buffer->allocated); ssh_burn(buffer->data, buffer->allocated);
SAFE_FREE(buffer->data); SAFE_FREE(buffer->data);
explicit_bzero(buffer, sizeof(struct ssh_buffer_struct)); ssh_burn(buffer, sizeof(struct ssh_buffer_struct));
} else { } else {
SAFE_FREE(buffer->data); SAFE_FREE(buffer->data);
} }
@@ -205,7 +205,7 @@ static int realloc_buffer(struct ssh_buffer_struct *buffer, uint32_t needed)
return -1; return -1;
} }
memcpy(new, buffer->data, buffer->used); memcpy(new, buffer->data, buffer->used);
explicit_bzero(buffer->data, buffer->used); ssh_burn(buffer->data, buffer->used);
SAFE_FREE(buffer->data); SAFE_FREE(buffer->data);
} else { } else {
new = realloc(buffer->data, needed); new = realloc(buffer->data, needed);
@@ -241,7 +241,7 @@ static void buffer_shift(ssh_buffer buffer)
if (buffer->secure) { if (buffer->secure) {
void *ptr = buffer->data + buffer->used; void *ptr = buffer->data + buffer->used;
explicit_bzero(ptr, burn_pos); ssh_burn(ptr, burn_pos);
} }
buffer_verify(buffer); buffer_verify(buffer);
@@ -266,7 +266,7 @@ int ssh_buffer_reinit(struct ssh_buffer_struct *buffer)
buffer_verify(buffer); buffer_verify(buffer);
if (buffer->secure && buffer->allocated > 0) { if (buffer->secure && buffer->allocated > 0) {
explicit_bzero(buffer->data, buffer->allocated); ssh_burn(buffer->data, buffer->allocated);
} }
buffer->used = 0; buffer->used = 0;
buffer->pos = 0; buffer->pos = 0;
@@ -1352,28 +1352,28 @@ cleanup:
case 'b': case 'b':
o.byte = va_arg(ap_copy, uint8_t *); o.byte = va_arg(ap_copy, uint8_t *);
if (buffer->secure) { if (buffer->secure) {
explicit_bzero(o.byte, sizeof(uint8_t)); ssh_burn(o.byte, sizeof(uint8_t));
break; break;
} }
break; break;
case 'w': case 'w':
o.word = va_arg(ap_copy, uint16_t *); o.word = va_arg(ap_copy, uint16_t *);
if (buffer->secure) { if (buffer->secure) {
explicit_bzero(o.word, sizeof(uint16_t)); ssh_burn(o.word, sizeof(uint16_t));
break; break;
} }
break; break;
case 'd': case 'd':
o.dword = va_arg(ap_copy, uint32_t *); o.dword = va_arg(ap_copy, uint32_t *);
if (buffer->secure) { if (buffer->secure) {
explicit_bzero(o.dword, sizeof(uint32_t)); ssh_burn(o.dword, sizeof(uint32_t));
break; break;
} }
break; break;
case 'q': case 'q':
o.qword = va_arg(ap_copy, uint64_t *); o.qword = va_arg(ap_copy, uint64_t *);
if (buffer->secure) { if (buffer->secure) {
explicit_bzero(o.qword, sizeof(uint64_t)); ssh_burn(o.qword, sizeof(uint64_t));
break; break;
} }
break; break;
@@ -1391,7 +1391,7 @@ cleanup:
case 's': case 's':
o.cstring = va_arg(ap_copy, char **); o.cstring = va_arg(ap_copy, char **);
if (buffer->secure) { if (buffer->secure) {
explicit_bzero(*o.cstring, strlen(*o.cstring)); ssh_burn(*o.cstring, strlen(*o.cstring));
} }
SAFE_FREE(*o.cstring); SAFE_FREE(*o.cstring);
break; break;
@@ -1399,7 +1399,7 @@ cleanup:
len = va_arg(ap_copy, size_t); len = va_arg(ap_copy, size_t);
o.data = va_arg(ap_copy, void **); o.data = va_arg(ap_copy, void **);
if (buffer->secure) { if (buffer->secure) {
explicit_bzero(*o.data, len); ssh_burn(*o.data, len);
} }
SAFE_FREE(*o.data); SAFE_FREE(*o.data);
break; break;

View File

@@ -60,101 +60,102 @@
struct ssh_config_keyword_table_s { struct ssh_config_keyword_table_s {
const char *name; const char *name;
enum ssh_config_opcode_e opcode; enum ssh_config_opcode_e opcode;
bool cli_supported;
}; };
static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = { static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{ "host", SOC_HOST }, {"host", SOC_HOST, true},
{ "match", SOC_MATCH }, {"match", SOC_MATCH, false},
{ "hostname", SOC_HOSTNAME }, {"hostname", SOC_HOSTNAME, true},
{ "port", SOC_PORT }, {"port", SOC_PORT, true},
{ "user", SOC_USERNAME }, {"user", SOC_USERNAME, true},
{ "identityfile", SOC_IDENTITY }, {"identityfile", SOC_IDENTITY, true},
{ "ciphers", SOC_CIPHERS }, {"ciphers", SOC_CIPHERS, true},
{ "macs", SOC_MACS }, {"macs", SOC_MACS, true},
{ "compression", SOC_COMPRESSION }, {"compression", SOC_COMPRESSION, true},
{ "connecttimeout", SOC_TIMEOUT }, {"connecttimeout", SOC_TIMEOUT, true},
{ "stricthostkeychecking", SOC_STRICTHOSTKEYCHECK }, {"stricthostkeychecking", SOC_STRICTHOSTKEYCHECK, true},
{ "userknownhostsfile", SOC_KNOWNHOSTS }, {"userknownhostsfile", SOC_KNOWNHOSTS, true},
{ "proxycommand", SOC_PROXYCOMMAND }, {"proxycommand", SOC_PROXYCOMMAND, true},
{ "gssapiserveridentity", SOC_GSSAPISERVERIDENTITY }, {"gssapiserveridentity", SOC_GSSAPISERVERIDENTITY, false},
{ "gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY }, {"gssapiclientidentity", SOC_GSSAPICLIENTIDENTITY, false},
{ "gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS }, {"gssapidelegatecredentials", SOC_GSSAPIDELEGATECREDENTIALS, true},
{ "include", SOC_INCLUDE }, {"include", SOC_INCLUDE, true},
{ "bindaddress", SOC_BINDADDRESS}, {"bindaddress", SOC_BINDADDRESS, true},
{ "globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE}, {"globalknownhostsfile", SOC_GLOBALKNOWNHOSTSFILE, true},
{ "loglevel", SOC_LOGLEVEL}, {"loglevel", SOC_LOGLEVEL, true},
{ "hostkeyalgorithms", SOC_HOSTKEYALGORITHMS}, {"hostkeyalgorithms", SOC_HOSTKEYALGORITHMS, true},
{ "kexalgorithms", SOC_KEXALGORITHMS}, {"kexalgorithms", SOC_KEXALGORITHMS, true},
{ "gssapiauthentication", SOC_GSSAPIAUTHENTICATION}, {"gssapiauthentication", SOC_GSSAPIAUTHENTICATION, true},
{ "kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION}, {"kbdinteractiveauthentication", SOC_KBDINTERACTIVEAUTHENTICATION, true},
{ "passwordauthentication", SOC_PASSWORDAUTHENTICATION}, {"passwordauthentication", SOC_PASSWORDAUTHENTICATION, true},
{ "pubkeyauthentication", SOC_PUBKEYAUTHENTICATION}, {"pubkeyauthentication", SOC_PUBKEYAUTHENTICATION, true},
{ "addkeystoagent", SOC_UNSUPPORTED}, {"addkeystoagent", SOC_UNSUPPORTED, true},
{ "addressfamily", SOC_UNSUPPORTED}, {"addressfamily", SOC_UNSUPPORTED, true},
{ "batchmode", SOC_UNSUPPORTED}, {"batchmode", SOC_UNSUPPORTED, true},
{ "canonicaldomains", SOC_UNSUPPORTED}, {"canonicaldomains", SOC_UNSUPPORTED, true},
{ "canonicalizefallbacklocal", SOC_UNSUPPORTED}, {"canonicalizefallbacklocal", SOC_UNSUPPORTED, true},
{ "canonicalizehostname", SOC_UNSUPPORTED}, {"canonicalizehostname", SOC_UNSUPPORTED, true},
{ "canonicalizemaxdots", SOC_UNSUPPORTED}, {"canonicalizemaxdots", SOC_UNSUPPORTED, true},
{ "canonicalizepermittedcnames", SOC_UNSUPPORTED}, {"canonicalizepermittedcnames", SOC_UNSUPPORTED, true},
{ "certificatefile", SOC_CERTIFICATE}, {"certificatefile", SOC_CERTIFICATE, true},
{ "kbdinteractiveauthentication", SOC_UNSUPPORTED}, {"kbdinteractiveauthentication", SOC_UNSUPPORTED, true},
{ "checkhostip", SOC_UNSUPPORTED}, {"checkhostip", SOC_UNSUPPORTED, true},
{ "connectionattempts", SOC_UNSUPPORTED}, {"connectionattempts", SOC_UNSUPPORTED, true},
{ "enablesshkeysign", SOC_UNSUPPORTED}, {"enablesshkeysign", SOC_UNSUPPORTED, true},
{ "fingerprinthash", SOC_UNSUPPORTED}, {"fingerprinthash", SOC_UNSUPPORTED, true},
{ "forwardagent", SOC_UNSUPPORTED}, {"forwardagent", SOC_UNSUPPORTED, true},
{ "hashknownhosts", SOC_UNSUPPORTED}, {"hashknownhosts", SOC_UNSUPPORTED, true},
{ "hostbasedauthentication", SOC_UNSUPPORTED}, {"hostbasedauthentication", SOC_UNSUPPORTED, true},
{ "hostbasedacceptedalgorithms", SOC_UNSUPPORTED}, {"hostbasedacceptedalgorithms", SOC_UNSUPPORTED, true},
{ "hostkeyalias", SOC_UNSUPPORTED}, {"hostkeyalias", SOC_UNSUPPORTED, true},
{ "identitiesonly", SOC_IDENTITIESONLY}, {"identitiesonly", SOC_IDENTITIESONLY, true},
{ "identityagent", SOC_IDENTITYAGENT}, {"identityagent", SOC_IDENTITYAGENT, true},
{ "ipqos", SOC_UNSUPPORTED}, {"ipqos", SOC_UNSUPPORTED, true},
{ "kbdinteractivedevices", SOC_UNSUPPORTED}, {"kbdinteractivedevices", SOC_UNSUPPORTED, true},
{ "nohostauthenticationforlocalhost", SOC_UNSUPPORTED}, {"nohostauthenticationforlocalhost", SOC_UNSUPPORTED, true},
{ "numberofpasswordprompts", SOC_UNSUPPORTED}, {"numberofpasswordprompts", SOC_UNSUPPORTED, true},
{ "pkcs11provider", SOC_UNSUPPORTED}, {"pkcs11provider", SOC_UNSUPPORTED, true},
{ "preferredauthentications", SOC_UNSUPPORTED}, {"preferredauthentications", SOC_UNSUPPORTED, true},
{ "proxyjump", SOC_PROXYJUMP}, {"proxyjump", SOC_PROXYJUMP, true},
{ "proxyusefdpass", SOC_UNSUPPORTED}, {"proxyusefdpass", SOC_UNSUPPORTED, true},
{ "pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES}, {"pubkeyacceptedalgorithms", SOC_PUBKEYACCEPTEDKEYTYPES, true},
{ "rekeylimit", SOC_REKEYLIMIT}, {"rekeylimit", SOC_REKEYLIMIT, true},
{ "remotecommand", SOC_UNSUPPORTED}, {"remotecommand", SOC_UNSUPPORTED, true},
{ "revokedhostkeys", SOC_UNSUPPORTED}, {"revokedhostkeys", SOC_UNSUPPORTED, true},
{ "serveralivecountmax", SOC_UNSUPPORTED}, {"serveralivecountmax", SOC_UNSUPPORTED, true},
{ "serveraliveinterval", SOC_UNSUPPORTED}, {"serveraliveinterval", SOC_UNSUPPORTED, true},
{ "streamlocalbindmask", SOC_UNSUPPORTED}, {"streamlocalbindmask", SOC_UNSUPPORTED, true},
{ "streamlocalbindunlink", SOC_UNSUPPORTED}, {"streamlocalbindunlink", SOC_UNSUPPORTED, true},
{ "syslogfacility", SOC_UNSUPPORTED}, {"syslogfacility", SOC_UNSUPPORTED, true},
{ "tcpkeepalive", SOC_UNSUPPORTED}, {"tcpkeepalive", SOC_UNSUPPORTED, true},
{ "updatehostkeys", SOC_UNSUPPORTED}, {"updatehostkeys", SOC_UNSUPPORTED, true},
{ "verifyhostkeydns", SOC_UNSUPPORTED}, {"verifyhostkeydns", SOC_UNSUPPORTED, true},
{ "visualhostkey", SOC_UNSUPPORTED}, {"visualhostkey", SOC_UNSUPPORTED, true},
{ "clearallforwardings", SOC_NA}, {"clearallforwardings", SOC_NA, true},
{ "controlmaster", SOC_NA}, {"controlmaster", SOC_NA, true},
{ "controlpersist", SOC_NA}, {"controlpersist", SOC_NA, true},
{ "controlpath", SOC_NA}, {"controlpath", SOC_NA, true},
{ "dynamicforward", SOC_NA}, {"dynamicforward", SOC_NA, true},
{ "escapechar", SOC_NA}, {"escapechar", SOC_NA, true},
{ "exitonforwardfailure", SOC_NA}, {"exitonforwardfailure", SOC_NA, true},
{ "forwardx11", SOC_NA}, {"forwardx11", SOC_NA, true},
{ "forwardx11timeout", SOC_NA}, {"forwardx11timeout", SOC_NA, true},
{ "forwardx11trusted", SOC_NA}, {"forwardx11trusted", SOC_NA, true},
{ "gatewayports", SOC_NA}, {"gatewayports", SOC_NA, true},
{ "ignoreunknown", SOC_NA}, {"ignoreunknown", SOC_NA, true},
{ "localcommand", SOC_NA}, {"localcommand", SOC_NA, true},
{ "localforward", SOC_NA}, {"localforward", SOC_NA, true},
{ "permitlocalcommand", SOC_NA}, {"permitlocalcommand", SOC_NA, true},
{ "remoteforward", SOC_NA}, {"remoteforward", SOC_NA, true},
{ "requesttty", SOC_NA}, {"requesttty", SOC_NA, true},
{ "sendenv", SOC_NA}, {"sendenv", SOC_NA, true},
{ "tunnel", SOC_NA}, {"tunnel", SOC_NA, true},
{ "tunneldevice", SOC_NA}, {"tunneldevice", SOC_NA, true},
{ "xauthlocation", SOC_NA}, {"xauthlocation", SOC_NA, true},
{ "pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES}, {"pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES, true},
{ "requiredrsasize", SOC_REQUIRED_RSA_SIZE}, {"requiredrsasize", SOC_REQUIRED_RSA_SIZE, true},
{ NULL, SOC_UNKNOWN } {NULL, SOC_UNKNOWN, false},
}; };
enum ssh_config_match_e { enum ssh_config_match_e {
@@ -189,19 +190,48 @@ static struct ssh_config_match_keyword_table_s
{NULL, MATCH_UNKNOWN}, {NULL, MATCH_UNKNOWN},
}; };
static int ssh_config_parse_line(ssh_session session, const char *line, int ssh_config_parse_line(ssh_session session,
unsigned int count, int *parsing, unsigned int depth, bool global); const char *line,
unsigned int count,
int *parsing,
unsigned int depth,
bool global);
static enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword) { static int ssh_config_parse_line_internal(ssh_session session,
int i; const char *line,
unsigned int count,
int *parsing,
unsigned int depth,
bool global,
bool is_cli,
bool fail_on_unknown);
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) { int ssh_config_parse_line_cli(ssh_session session, const char *line);
if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
return ssh_config_keyword_table[i].opcode; enum ssh_config_opcode_e ssh_config_get_opcode(char *keyword)
{
int i;
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
if (strcasecmp(keyword, ssh_config_keyword_table[i].name) == 0) {
return ssh_config_keyword_table[i].opcode;
}
} }
}
return SOC_UNKNOWN; return SOC_UNKNOWN;
}
static bool ssh_config_is_cli_supported(enum ssh_config_opcode_e opcode)
{
int i;
for (i = 0; ssh_config_keyword_table[i].name != NULL; i++) {
if (opcode == ssh_config_keyword_table[i].opcode) {
return ssh_config_keyword_table[i].cli_supported;
}
}
return false;
} }
#define LIBSSH_CONF_MAX_DEPTH 16 #define LIBSSH_CONF_MAX_DEPTH 16
@@ -735,13 +765,78 @@ ssh_match_localnetwork(const char *addrlist, bool negate)
} }
#endif /* HAVE_IFADDRS_H */ #endif /* HAVE_IFADDRS_H */
static int static enum ssh_options_e
ssh_config_parse_line(ssh_session session, ssh_config_get_auth_option(enum ssh_config_opcode_e opcode)
const char *line, {
unsigned int count, struct auth_option_map {
int *parsing, enum ssh_config_opcode_e opcode;
unsigned int depth, const char *name;
bool global) enum ssh_options_e option;
};
static struct auth_option_map auth_options[] = {
{
SOC_GSSAPIAUTHENTICATION,
"GSSAPIAuthentication",
SSH_OPTIONS_GSSAPI_AUTH,
},
{
SOC_KBDINTERACTIVEAUTHENTICATION,
"KbdInteractiveAuthentication",
SSH_OPTIONS_KBDINT_AUTH,
},
{
SOC_PASSWORDAUTHENTICATION,
"PasswordAuthentication",
SSH_OPTIONS_PASSWORD_AUTH,
},
{
SOC_PUBKEYAUTHENTICATION,
"PubkeyAuthentication",
SSH_OPTIONS_PUBKEY_AUTH,
},
{0, NULL, 0},
};
for (struct auth_option_map *map = auth_options; map->name != NULL; map++) {
if (map->opcode == opcode) {
return map->option;
}
}
return -1;
}
#define CHECK_COND_OR_FAIL(cond, error_message) \
do { \
if ((cond)) { \
SSH_LOG(SSH_LOG_DEBUG, \
"line %d: %s: %s", \
count, \
error_message, \
keyword); \
if (fail_on_unknown) { \
ssh_set_error(session, \
SSH_FATAL, \
is_cli ? "%s '%s' value on CLI" \
: "%s '%s' value at line %d", \
error_message, \
keyword, \
is_cli ? 0 : count); \
SAFE_FREE(x); \
return SSH_ERROR; \
} \
break; \
} \
} while (0)
static int ssh_config_parse_line_internal(ssh_session session,
const char *line,
unsigned int count,
int *parsing,
unsigned int depth,
bool global,
bool is_cli,
bool fail_on_unknown)
{ {
enum ssh_config_opcode_e opcode; enum ssh_config_opcode_e opcode;
const char *p = NULL, *p2 = NULL; const char *p = NULL, *p2 = NULL;
@@ -756,6 +851,9 @@ ssh_config_parse_line(ssh_session session,
/* Ignore empty lines */ /* Ignore empty lines */
if (line == NULL || *line == '\0') { if (line == NULL || *line == '\0') {
if (is_cli) {
return SSH_ERROR;
}
return 0; return 0;
} }
@@ -781,6 +879,16 @@ ssh_config_parse_line(ssh_session session,
} }
opcode = ssh_config_get_opcode(keyword); opcode = ssh_config_get_opcode(keyword);
if (is_cli && !ssh_config_is_cli_supported(opcode)) {
ssh_set_error(
session,
SSH_FATAL,
"Option '%s' is not supported in command-line configuration",
keyword);
SAFE_FREE(x);
return SSH_ERROR;
}
if (*parsing == 1 && if (*parsing == 1 &&
opcode != SOC_HOST && opcode != SOC_HOST &&
opcode != SOC_MATCH && opcode != SOC_MATCH &&
@@ -1051,82 +1159,93 @@ ssh_config_parse_line(ssh_session session,
} }
case SOC_HOSTNAME: case SOC_HOSTNAME:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
char *z = ssh_path_expand_escape(session, p); if (*parsing) {
if (z == NULL) { char *z = ssh_path_expand_escape(session, p);
z = strdup(p); if (z == NULL) {
} z = strdup(p);
ssh_options_set(session, SSH_OPTIONS_HOST, z); }
free(z); ssh_options_set(session, SSH_OPTIONS_HOST, z);
free(z);
} }
break; break;
case SOC_PORT: case SOC_PORT:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_PORT_STR, p); ssh_options_set(session, SSH_OPTIONS_PORT_STR, p);
} }
break; break;
case SOC_USERNAME: case SOC_USERNAME:
if (session->opts.username == NULL) { if (session->opts.username == NULL) {
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_USER, p); if (*parsing) {
} ssh_options_set(session, SSH_OPTIONS_USER, p);
}
} }
break; break;
case SOC_IDENTITY: case SOC_IDENTITY:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, p);
} }
break; break;
case SOC_CIPHERS: case SOC_CIPHERS:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p); ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, p);
ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, p);
} }
break; break;
case SOC_MACS: case SOC_MACS:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p); ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, p);
ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, p);
} }
break; break;
case SOC_COMPRESSION: case SOC_COMPRESSION:
i = ssh_config_get_yesno(&s, -1); i = ssh_config_get_yesno(&s, -1);
if (i >= 0 && *parsing) { CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
if (i) { if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes"); if (i) {
} else { ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no"); } else {
} ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "no");
}
} }
break; break;
case SOC_TIMEOUT: case SOC_TIMEOUT:
l = ssh_config_get_long(&s, -1); l = ssh_config_get_long(&s, -1);
if (l >= 0 && *parsing) { CHECK_COND_OR_FAIL(l < 0, "Invalid argument");
ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &l);
} }
break; break;
case SOC_STRICTHOSTKEYCHECK: case SOC_STRICTHOSTKEYCHECK:
i = ssh_config_get_yesno(&s, -1); i = ssh_config_get_yesno(&s, -1);
if (i >= 0 && *parsing) { CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &i);
} }
break; break;
case SOC_KNOWNHOSTS: case SOC_KNOWNHOSTS:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p);
} }
break; break;
case SOC_PROXYCOMMAND: case SOC_PROXYCOMMAND:
p = ssh_config_get_cmd(&s); p = ssh_config_get_cmd(&s);
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
/* We share the seen value with the ProxyJump */ /* We share the seen value with the ProxyJump */
if (p && *parsing && !seen[SOC_PROXYJUMP]) { if (*parsing && !seen[SOC_PROXYJUMP]) {
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p); ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p);
} }
break; break;
case SOC_PROXYJUMP: case SOC_PROXYJUMP:
@@ -1146,37 +1265,43 @@ ssh_config_parse_line(ssh_session session,
break; break;
case SOC_GSSAPISERVERIDENTITY: case SOC_GSSAPISERVERIDENTITY:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GSSAPI_SERVER_IDENTITY, p);
} }
break; break;
case SOC_GSSAPICLIENTIDENTITY: case SOC_GSSAPICLIENTIDENTITY:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY, p);
} }
break; break;
case SOC_GSSAPIDELEGATECREDENTIALS: case SOC_GSSAPIDELEGATECREDENTIALS:
i = ssh_config_get_yesno(&s, -1); i = ssh_config_get_yesno(&s, -1);
if (i >=0 && *parsing) { CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i); if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS, &i);
} }
break; break;
case SOC_BINDADDRESS: case SOC_BINDADDRESS:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_BINDADDR, p); ssh_options_set(session, SSH_OPTIONS_BINDADDR, p);
} }
break; break;
case SOC_GLOBALKNOWNHOSTSFILE: case SOC_GLOBALKNOWNHOSTSFILE:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, p); ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, p);
} }
break; break;
case SOC_LOGLEVEL: case SOC_LOGLEVEL:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
int value = -1; int value = -1;
if (strcasecmp(p, "quiet") == 0) { if (strcasecmp(p, "quiet") == 0) {
@@ -1194,6 +1319,7 @@ ssh_config_parse_line(ssh_session session,
strcasecmp(p, "DEBUG3") == 0) { strcasecmp(p, "DEBUG3") == 0) {
value = SSH_LOG_TRACE; value = SSH_LOG_TRACE;
} }
CHECK_COND_OR_FAIL(value == -1, "Invalid value");
if (value != -1) { if (value != -1) {
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &value); ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &value);
} }
@@ -1201,19 +1327,22 @@ ssh_config_parse_line(ssh_session session,
break; break;
case SOC_HOSTKEYALGORITHMS: case SOC_HOSTKEYALGORITHMS:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p); ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, p);
} }
break; break;
case SOC_PUBKEYACCEPTEDKEYTYPES: case SOC_PUBKEYACCEPTEDKEYTYPES:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p); ssh_options_set(session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, p);
} }
break; break;
case SOC_KEXALGORITHMS: case SOC_KEXALGORITHMS:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, p); ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, p);
} }
break; break;
@@ -1221,6 +1350,7 @@ ssh_config_parse_line(ssh_session session,
/* Parse the data limit */ /* Parse the data limit */
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL) { if (p == NULL) {
CHECK_COND_OR_FAIL(1, "Missing data limit");
break; break;
} else if (strcmp(p, "default") == 0) { } else if (strcmp(p, "default") == 0) {
/* Default rekey limits enforced automatically */ /* Default rekey limits enforced automatically */
@@ -1229,8 +1359,7 @@ ssh_config_parse_line(ssh_session session,
char *endp = NULL; char *endp = NULL;
ll = strtoll(p, &endp, 10); ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) { if (p == endp || ll < 0) {
/* No number or negative */ CHECK_COND_OR_FAIL(1, "Invalid data limit");
SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit");
break; break;
} }
switch (*endp) { switch (*endp) {
@@ -1268,19 +1397,19 @@ ssh_config_parse_line(ssh_session session,
break; break;
} }
if (*endp != ' ' && *endp != '\0') { if (*endp != ' ' && *endp != '\0') {
SSH_LOG(SSH_LOG_TRACE, CHECK_COND_OR_FAIL(1, "Invalid trailing characters");
"Invalid trailing characters after the rekey limit: %s",
endp);
break; break;
} }
} }
if (ll > -1 && *parsing) { CHECK_COND_OR_FAIL(ll < 0, "Invalid data limit");
if (*parsing) {
uint64_t v = (uint64_t)ll; uint64_t v = (uint64_t)ll;
ssh_options_set(session, SSH_OPTIONS_REKEY_DATA, &v); ssh_options_set(session, SSH_OPTIONS_REKEY_DATA, &v);
} }
/* Parse the time limit */ /* Parse the time limit */
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL) { if (p == NULL) {
CHECK_COND_OR_FAIL(1, "Missing time limit");
break; break;
} else if (strcmp(p, "none") == 0) { } else if (strcmp(p, "none") == 0) {
ll = 0; ll = 0;
@@ -1289,7 +1418,7 @@ ssh_config_parse_line(ssh_session session,
ll = strtoll(p, &endp, 10); ll = strtoll(p, &endp, 10);
if (p == endp || ll < 0) { if (p == endp || ll < 0) {
/* No number or negative */ /* No number or negative */
SSH_LOG(SSH_LOG_TRACE, "Invalid argument to rekey limit"); CHECK_COND_OR_FAIL(1, "Invalid time limit");
break; break;
} }
switch (*endp) { switch (*endp) {
@@ -1342,11 +1471,11 @@ ssh_config_parse_line(ssh_session session,
break; break;
} }
if (*endp != '\0') { if (*endp != '\0') {
SSH_LOG(SSH_LOG_TRACE, "Invalid trailing characters after the" CHECK_COND_OR_FAIL(1, "Invalid trailing characters");
" rekey limit: %s", endp);
break; break;
} }
} }
CHECK_COND_OR_FAIL(ll < 0, "Invalid time limit");
if (ll > -1 && *parsing) { if (ll > -1 && *parsing) {
uint32_t v = (uint32_t)ll; uint32_t v = (uint32_t)ll;
ssh_options_set(session, SSH_OPTIONS_REKEY_TIME, &v); ssh_options_set(session, SSH_OPTIONS_REKEY_TIME, &v);
@@ -1355,56 +1484,44 @@ ssh_config_parse_line(ssh_session session,
case SOC_GSSAPIAUTHENTICATION: case SOC_GSSAPIAUTHENTICATION:
case SOC_KBDINTERACTIVEAUTHENTICATION: case SOC_KBDINTERACTIVEAUTHENTICATION:
case SOC_PASSWORDAUTHENTICATION: case SOC_PASSWORDAUTHENTICATION:
case SOC_PUBKEYAUTHENTICATION: case SOC_PUBKEYAUTHENTICATION: {
enum ssh_options_e option = ssh_config_get_auth_option(opcode);
i = ssh_config_get_yesno(&s, 0); i = ssh_config_get_yesno(&s, 0);
if (i>=0 && *parsing) {
switch(opcode){ CHECK_COND_OR_FAIL(i < 0, "Authentication option");
case SOC_GSSAPIAUTHENTICATION: if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GSSAPI_AUTH, &i); ssh_options_set(session, option, &i);
break;
case SOC_KBDINTERACTIVEAUTHENTICATION:
ssh_options_set(session, SSH_OPTIONS_KBDINT_AUTH, &i);
break;
case SOC_PASSWORDAUTHENTICATION:
ssh_options_set(session, SSH_OPTIONS_PASSWORD_AUTH, &i);
break;
case SOC_PUBKEYAUTHENTICATION:
ssh_options_set(session, SSH_OPTIONS_PUBKEY_AUTH, &i);
break;
/* make gcc happy */
default:
break;
}
} }
break; break;
}
case SOC_NA: case SOC_NA:
SSH_LOG(SSH_LOG_TRACE, "Unapplicable option: %s, line: %d", CHECK_COND_OR_FAIL(1, "Unapplicable option");
keyword, count); break;
break;
case SOC_UNSUPPORTED: case SOC_UNSUPPORTED:
SSH_LOG(SSH_LOG_RARE, "Unsupported option: %s, line: %d", CHECK_COND_OR_FAIL(1, "Unsupported option");
keyword, count); break;
break;
case SOC_UNKNOWN: case SOC_UNKNOWN:
SSH_LOG(SSH_LOG_TRACE, "Unknown option: %s, line: %d", CHECK_COND_OR_FAIL(1, "Unknown option");
keyword, count); break;
break;
case SOC_IDENTITYAGENT: case SOC_IDENTITYAGENT:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p); ssh_options_set(session, SSH_OPTIONS_IDENTITY_AGENT, p);
} }
break; break;
case SOC_IDENTITIESONLY: case SOC_IDENTITIESONLY:
i = ssh_config_get_yesno(&s, -1); i = ssh_config_get_yesno(&s, -1);
if (i >= 0 && *parsing) { CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
bool b = i; if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b); bool b = i;
ssh_options_set(session, SSH_OPTIONS_IDENTITIES_ONLY, &b);
} }
break; break;
case SOC_CONTROLMASTER: case SOC_CONTROLMASTER:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "ControlMaster");
if (*parsing) {
int value = -1; int value = -1;
if (strcasecmp(p, "auto") == 0) { if (strcasecmp(p, "auto") == 0) {
@@ -1419,6 +1536,7 @@ ssh_config_parse_line(ssh_session session,
value = SSH_CONTROL_MASTER_ASK; value = SSH_CONTROL_MASTER_ASK;
} }
CHECK_COND_OR_FAIL(value == -1, "Invalid argument");
if (value != -1) { if (value != -1) {
ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value); ssh_options_set(session, SSH_OPTIONS_CONTROL_MASTER, &value);
} }
@@ -1436,13 +1554,15 @@ ssh_config_parse_line(ssh_session session,
break; break;
case SOC_CERTIFICATE: case SOC_CERTIFICATE:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p); ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
} }
break; break;
case SOC_REQUIRED_RSA_SIZE: case SOC_REQUIRED_RSA_SIZE:
l = ssh_config_get_long(&s, -1); l = ssh_config_get_long(&s, -1);
if (l >= 0 && *parsing) { CHECK_COND_OR_FAIL(l < 0, "Invalid argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &l); ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &l);
} }
break; break;
@@ -1458,6 +1578,38 @@ ssh_config_parse_line(ssh_session session,
return 0; return 0;
} }
#undef SSH_PARSE_OR_FAIL
int ssh_config_parse_line(ssh_session session,
const char *line,
unsigned int count,
int *parsing,
unsigned int depth,
bool global)
{
return ssh_config_parse_line_internal(session,
line,
count,
parsing,
depth,
global,
false,
false);
}
int ssh_config_parse_line_cli(ssh_session session, const char *line)
{
int parsing = 1;
return ssh_config_parse_line_internal(session,
line,
0,
&parsing,
0,
false,
true,
true);
}
/* @brief Parse configuration from a file pointer /* @brief Parse configuration from a file pointer
* *
* @params[in] session The ssh session * @params[in] session The ssh session

View File

@@ -97,8 +97,8 @@ bcrypt_hash(ssh_blf_ctx *state, uint8_t *sha2pass, uint8_t *sha2salt, uint8_t *o
} }
/* zap */ /* zap */
explicit_bzero(ciphertext, sizeof(ciphertext)); ssh_burn(ciphertext, sizeof(ciphertext));
explicit_bzero(cdata, sizeof(cdata)); ssh_burn(cdata, sizeof(cdata));
} }
int int
@@ -180,12 +180,12 @@ bcrypt_pbkdf(const char *pass, size_t passlen, const uint8_t *salt, size_t saltl
} }
/* zap */ /* zap */
explicit_bzero(out, sizeof(out)); ssh_burn(out, sizeof(out));
explicit_bzero(state, sizeof(*state)); ssh_burn(state, sizeof(*state));
free(state); free(state);
free(countsalt); free(countsalt);
return 0; return 0;
} }
#endif /* HAVE_BCRYPT_PBKDF */ #endif /* HAVE_BCRYPT_PBKDF */

View File

@@ -88,7 +88,7 @@ static int ssh_gets(const char *prompt, char *buf, size_t len, int verify)
fprintf(stdout, "\nVerifying, please re-enter. %s", prompt); fprintf(stdout, "\nVerifying, please re-enter. %s", prompt);
fflush(stdout); fflush(stdout);
if (!fgets(key_string, (int)len, stdin)) { if (!fgets(key_string, (int)len, stdin)) {
explicit_bzero(key_string, len); ssh_burn(key_string, len);
SAFE_FREE(key_string); SAFE_FREE(key_string);
clearerr(stdin); clearerr(stdin);
continue; continue;
@@ -99,17 +99,17 @@ static int ssh_gets(const char *prompt, char *buf, size_t len, int verify)
fprintf(stdout, "\n"); fprintf(stdout, "\n");
if (strcmp(buf, key_string)) { if (strcmp(buf, key_string)) {
printf("\n\07\07Mismatch - try again\n"); printf("\n\07\07Mismatch - try again\n");
explicit_bzero(key_string, len); ssh_burn(key_string, len);
SAFE_FREE(key_string); SAFE_FREE(key_string);
fflush(stdout); fflush(stdout);
continue; continue;
} }
explicit_bzero(key_string, len); ssh_burn(key_string, len);
SAFE_FREE(key_string); SAFE_FREE(key_string);
} }
ok = 1; ok = 1;
} }
explicit_bzero(tmp, len); ssh_burn(tmp, len);
free(tmp); free(tmp);
return ok; return ok;
@@ -152,7 +152,7 @@ int ssh_getpass(const char *prompt,
SetConsoleMode(h, mode); SetConsoleMode(h, mode);
if (!ok) { if (!ok) {
explicit_bzero(buf, len); ssh_burn(buf, len);
return -1; return -1;
} }
@@ -257,8 +257,8 @@ int ssh_getpass(const char *prompt,
} }
/* disable nonblocking I/O */ /* disable nonblocking I/O */
if (fd & O_NDELAY) { if (fd & O_NONBLOCK) {
ok = fcntl(0, F_SETFL, fd & ~O_NDELAY); ok = fcntl(0, F_SETFL, fd & ~O_NONBLOCK);
if (ok < 0) { if (ok < 0) {
perror("fcntl"); perror("fcntl");
return -1; return -1;
@@ -273,7 +273,7 @@ int ssh_getpass(const char *prompt,
} }
/* close fd */ /* close fd */
if (fd & O_NDELAY) { if (fd & O_NONBLOCK) {
ok = fcntl(0, F_SETFL, fd); ok = fcntl(0, F_SETFL, fd);
if (ok < 0) { if (ok < 0) {
perror("fcntl"); perror("fcntl");
@@ -282,7 +282,7 @@ int ssh_getpass(const char *prompt,
} }
if (!ok) { if (!ok) {
explicit_bzero(buf, len); ssh_burn(buf, len);
return -1; return -1;
} }

View File

@@ -487,7 +487,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_hybrid_mlkem_reply)
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
cleanup: cleanup:
explicit_bzero(mlkem_shared_secret, sizeof(mlkem_shared_secret)); ssh_burn(mlkem_shared_secret, sizeof(mlkem_shared_secret));
ssh_string_burn(ecdh_shared_secret); ssh_string_burn(ecdh_shared_secret);
ssh_string_free(ecdh_shared_secret); ssh_string_free(ecdh_shared_secret);
ssh_string_free(pubkey_blob); ssh_string_free(pubkey_blob);
@@ -851,7 +851,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
cleanup: cleanup:
explicit_bzero(mlkem_shared_secret, sizeof(mlkem_shared_secret)); ssh_burn(mlkem_shared_secret, sizeof(mlkem_shared_secret));
ssh_string_burn(ecdh_shared_secret); ssh_string_burn(ecdh_shared_secret);
ssh_string_free(ecdh_shared_secret); ssh_string_free(ecdh_shared_secret);
ssh_string_free(pubkey_blob); ssh_string_free(pubkey_blob);

View File

@@ -376,25 +376,23 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
} }
} }
host_port = ssh_session_get_host_port(session);
if (host_port == NULL) {
return NULL;
}
list = ssh_list_new(); list = ssh_list_new();
if (list == NULL) { if (list == NULL) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
SAFE_FREE(host_port);
return NULL; return NULL;
} }
host_port = ssh_session_get_host_port(session);
if (host_port == NULL) {
goto error;
}
rc = ssh_known_hosts_read_entries(host_port, rc = ssh_known_hosts_read_entries(host_port,
session->opts.knownhosts, session->opts.knownhosts,
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); SAFE_FREE(host_port);
ssh_list_free(list); goto error;
return NULL;
} }
rc = ssh_known_hosts_read_entries(host_port, rc = ssh_known_hosts_read_entries(host_port,
@@ -402,21 +400,16 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
&entry_list); &entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); goto error;
ssh_list_free(list);
return NULL;
} }
if (entry_list == NULL) { if (entry_list == NULL) {
ssh_list_free(list); goto error;
return NULL;
} }
count = ssh_list_count(entry_list); count = ssh_list_count(entry_list);
if (count == 0) { if (count == 0) {
ssh_list_free(list); goto error;
ssh_list_free(entry_list);
return NULL;
} }
for (it = ssh_list_get_iterator(entry_list); for (it = ssh_list_get_iterator(entry_list);
@@ -460,6 +453,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
return list; return list;
error: error:
ssh_list_free(entry_list);
ssh_list_free(list); ssh_list_free(list);
return NULL; return NULL;
} }

View File

@@ -848,7 +848,7 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
return SSH_ERROR; return SSH_ERROR;
} }
#else #else
mac = EVP_MAC_fetch(NULL, "poly1305", NULL); mac = EVP_MAC_fetch(NULL, SN_poly1305, NULL);
if (mac == NULL) { if (mac == NULL) {
SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_fetch failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_fetch failed");
goto out; goto out;
@@ -970,7 +970,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
ret = SSH_OK; ret = SSH_OK;
out: out:
explicit_bzero(poly_key, sizeof(poly_key)); ssh_burn(poly_key, sizeof(poly_key));
return ret; return ret;
} }
@@ -1581,7 +1581,7 @@ evp_dup_pkey(const char *name, const ssh_key key, int demote, ssh_key new_key)
int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote) int evp_dup_rsa_pkey(const ssh_key key, ssh_key new_key, int demote)
{ {
return evp_dup_pkey("RSA", key, demote, new_key); return evp_dup_pkey(SN_rsa, key, demote, new_key);
} }
int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote) int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote)
@@ -1591,7 +1591,7 @@ int evp_dup_ecdsa_pkey(const ssh_key key, ssh_key new_key, int demote)
int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote) int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote)
{ {
return evp_dup_pkey("ED25519", key, demote, new_key); return evp_dup_pkey(SN_ED25519, key, demote, new_key);
} }
#endif /* OPENSSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
@@ -1633,15 +1633,14 @@ pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p)
int pki_key_ecgroup_name_to_nid(const char *group) int pki_key_ecgroup_name_to_nid(const char *group)
{ {
if (strcmp(group, NISTP256) == 0 || if (strcmp(group, NISTP256) == 0 || strcmp(group, "secp256r1") == 0 ||
strcmp(group, "secp256r1") == 0 || strcmp(group, SN_X9_62_prime256v1) == 0) {
strcmp(group, "prime256v1") == 0) {
return NID_X9_62_prime256v1; return NID_X9_62_prime256v1;
} else if (strcmp(group, NISTP384) == 0 || } else if (strcmp(group, NISTP384) == 0 ||
strcmp(group, "secp384r1") == 0) { strcmp(group, SN_secp384r1) == 0) {
return NID_secp384r1; return NID_secp384r1;
} else if (strcmp(group, NISTP521) == 0 || } else if (strcmp(group, NISTP521) == 0 ||
strcmp(group, "secp521r1") == 0) { strcmp(group, SN_secp521r1) == 0) {
return NID_secp521r1; return NID_secp521r1;
} }
return -1; return -1;

View File

@@ -602,7 +602,7 @@ static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
} }
out: out:
explicit_bzero(poly_key, sizeof(poly_key)); ssh_burn(poly_key, sizeof(poly_key));
} }
static int chacha20_poly1305_aead_decrypt_length( static int chacha20_poly1305_aead_decrypt_length(
@@ -714,7 +714,7 @@ static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
ret = SSH_OK; ret = SSH_OK;
out: out:
explicit_bzero(poly_key, sizeof(poly_key)); ssh_burn(poly_key, sizeof(poly_key));
return ret; return ret;
} }
#endif /* HAVE_GCRYPT_CHACHA_POLY */ #endif /* HAVE_GCRYPT_CHACHA_POLY */

View File

@@ -711,7 +711,7 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
ret = SSH_OK; ret = SSH_OK;
out: out:
explicit_bzero(poly_key, sizeof(poly_key)); ssh_burn(poly_key, sizeof(poly_key));
return ret; return ret;
} }

View File

@@ -670,9 +670,9 @@ void ssh_message_free(ssh_message msg){
SAFE_FREE(msg->auth_request.username); SAFE_FREE(msg->auth_request.username);
SAFE_FREE(msg->auth_request.sigtype); SAFE_FREE(msg->auth_request.sigtype);
if (msg->auth_request.password) { if (msg->auth_request.password) {
explicit_bzero(msg->auth_request.password, ssh_burn(msg->auth_request.password,
strlen(msg->auth_request.password)); strlen(msg->auth_request.password));
SAFE_FREE(msg->auth_request.password); SAFE_FREE(msg->auth_request.password);
} }
ssh_key_free(msg->auth_request.pubkey); ssh_key_free(msg->auth_request.pubkey);
ssh_key_free(msg->auth_request.server_pubkey); ssh_key_free(msg->auth_request.server_pubkey);
@@ -1236,9 +1236,9 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_info_response){
uint32_t n; uint32_t n;
for (n = 0; n < session->kbdint->nanswers; n++) { for (n = 0; n < session->kbdint->nanswers; n++) {
explicit_bzero(session->kbdint->answers[n], ssh_burn(session->kbdint->answers[n],
strlen(session->kbdint->answers[n])); strlen(session->kbdint->answers[n]));
SAFE_FREE(session->kbdint->answers[n]); SAFE_FREE(session->kbdint->answers[n]);
} }
SAFE_FREE(session->kbdint->answers); SAFE_FREE(session->kbdint->answers);
session->kbdint->nanswers = 0; session->kbdint->nanswers = 0;

View File

@@ -1608,23 +1608,6 @@ int ssh_timeout_update(struct ssh_timestamp *ts, int timeout)
return ret >= 0 ? ret: 0; return ret >= 0 ? ret: 0;
} }
#if !defined(HAVE_EXPLICIT_BZERO)
void explicit_bzero(void *s, size_t n)
{
#if defined(HAVE_MEMSET_S)
memset_s(s, n, '\0', n);
#elif defined(HAVE_SECURE_ZERO_MEMORY)
SecureZeroMemory(s, n);
#else
memset(s, '\0', n);
#if defined(HAVE_GCC_VOLATILE_MEMORY_PROTECTION)
/* See http://llvm.org/bugs/show_bug.cgi?id=15495 */
__asm__ volatile("" : : "g"(s) : "memory");
#endif /* HAVE_GCC_VOLATILE_MEMORY_PROTECTION */
#endif
}
#endif /* !HAVE_EXPLICIT_BZERO */
/** /**
* @brief Securely free memory by overwriting it before deallocation * @brief Securely free memory by overwriting it before deallocation
* *
@@ -1642,7 +1625,7 @@ void burn_free(void *ptr, size_t len)
return; return;
} }
explicit_bzero(ptr, len); ssh_burn(ptr, len);
free(ptr); free(ptr);
} }

View File

@@ -31,6 +31,7 @@
#else #else
#include <winsock2.h> #include <winsock2.h>
#endif #endif
#include "libssh/config.h"
#include "libssh/config_parser.h" #include "libssh/config_parser.h"
#include "libssh/misc.h" #include "libssh/misc.h"
#include "libssh/options.h" #include "libssh/options.h"
@@ -1698,6 +1699,7 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
int compress = 0; int compress = 0;
int cont = 1; int cont = 1;
size_t current = 0; size_t current = 0;
int opt_rc = 0;
int saveoptind = optind; /* need to save 'em */ int saveoptind = optind; /* need to save 'em */
int saveopterr = opterr; int saveopterr = opterr;
int opt; int opt;
@@ -1708,7 +1710,7 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
} }
opterr = 0; /* shut up getopt */ opterr = 0; /* shut up getopt */
while((opt = getopt(argc, argv, "c:i:Cl:p:vb:r12")) != -1) { while ((opt = getopt(argc, argv, "c:i:o:Cl:p:vb:r12")) != -1) {
switch(opt) { switch(opt) {
case 'l': case 'l':
user = optarg; user = optarg;
@@ -1718,6 +1720,7 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
break; break;
case 'v': case 'v':
debuglevel++; debuglevel++;
ssh_set_log_level(debuglevel);
break; break;
case 'r': case 'r':
break; break;
@@ -1730,6 +1733,9 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
case 'C': case 'C':
compress++; compress++;
break; break;
case 'o':
opt_rc = ssh_config_parse_line_cli(session, optarg);
break;
case '2': case '2':
break; break;
case '1': case '1':
@@ -1761,6 +1767,9 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
} }
} }
} /* switch */ } /* switch */
if (opt_rc == SSH_ERROR) {
break;
}
} /* while */ } /* while */
opterr = saveopterr; opterr = saveopterr;
tmp = realloc(save, (current + (argc - optind)) * sizeof(char*)); tmp = realloc(save, (current + (argc - optind)) * sizeof(char*));
@@ -1783,10 +1792,13 @@ int ssh_options_getopt(ssh_session session, int *argcptr, char **argv)
optind++; optind++;
} }
ssh_set_log_level(debuglevel);
optind = saveoptind; optind = saveoptind;
if (opt_rc == SSH_ERROR) {
SAFE_FREE(save);
return SSH_ERROR;
}
if(!cont) { if(!cont) {
SAFE_FREE(save); SAFE_FREE(save);
return -1; return -1;

View File

@@ -235,7 +235,7 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, size_t len)
#endif #endif
} }
} }
explicit_bzero(out, len); ssh_burn(out, len);
SAFE_FREE(out); SAFE_FREE(out);
return crypto->hmacbuf; return crypto->hmacbuf;

View File

@@ -227,7 +227,7 @@ void ssh_key_clean (ssh_key key)
#ifndef HAVE_LIBCRYPTO #ifndef HAVE_LIBCRYPTO
if (key->ed25519_privkey != NULL) { if (key->ed25519_privkey != NULL) {
explicit_bzero(key->ed25519_privkey, sizeof(ed25519_privkey)); ssh_burn(key->ed25519_privkey, sizeof(ed25519_privkey));
SAFE_FREE(key->ed25519_privkey); SAFE_FREE(key->ed25519_privkey);
} }
SAFE_FREE(key->ed25519_pubkey); SAFE_FREE(key->ed25519_pubkey);
@@ -3082,8 +3082,8 @@ int pki_sk_signature_buffer_prepare(const ssh_key key,
out: out:
SSH_BUFFER_FREE(sk_buffer); SSH_BUFFER_FREE(sk_buffer);
explicit_bzero(application_hash, SHA256_DIGEST_LEN); ssh_burn(application_hash, SHA256_DIGEST_LEN);
explicit_bzero(input_hash, SHA256_DIGEST_LEN); ssh_burn(input_hash, SHA256_DIGEST_LEN);
return ret; return ret;
} }

View File

@@ -208,7 +208,7 @@ static int pki_private_key_decrypt(ssh_string blob,
if (rc < 0){ if (rc < 0){
return SSH_ERROR; return SSH_ERROR;
} }
explicit_bzero(passphrase_buffer, sizeof(passphrase_buffer)); ssh_burn(passphrase_buffer, sizeof(passphrase_buffer));
cipher.set_decrypt_key(&cipher, cipher.set_decrypt_key(&cipher,
key_material, key_material,
@@ -487,7 +487,7 @@ static int pki_private_key_encrypt(ssh_buffer privkey_buffer,
ssh_buffer_get(privkey_buffer), ssh_buffer_get(privkey_buffer),
ssh_buffer_get_len(privkey_buffer)); ssh_buffer_get_len(privkey_buffer));
ssh_cipher_clear(&cipher); ssh_cipher_clear(&cipher);
explicit_bzero(passphrase_buffer, sizeof(passphrase_buffer)); ssh_burn(passphrase_buffer, sizeof(passphrase_buffer));
return SSH_OK; return SSH_OK;
} }
@@ -653,7 +653,7 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
"\n", "\n",
OPENSSH_HEADER_END, OPENSSH_HEADER_END,
"\n"); "\n");
explicit_bzero(b64, strlen((char *)b64)); ssh_burn(b64, strlen((char *)b64));
SAFE_FREE(b64); SAFE_FREE(b64);
if (rc != SSH_OK){ if (rc != SSH_OK){
@@ -677,7 +677,7 @@ error:
ssh_string_free(blob); ssh_string_free(blob);
if (privkey_buffer != NULL) { if (privkey_buffer != NULL) {
void *bufptr = ssh_buffer_get(privkey_buffer); void *bufptr = ssh_buffer_get(privkey_buffer);
explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer)); ssh_burn(bufptr, ssh_buffer_get_len(privkey_buffer));
SSH_BUFFER_FREE(privkey_buffer); SSH_BUFFER_FREE(privkey_buffer);
} }
SAFE_FREE(pubkey_s); SAFE_FREE(pubkey_s);

View File

@@ -1806,7 +1806,7 @@ ssh_string pki_key_to_blob(const ssh_key key, enum ssh_key_e type)
if (rc == SSH_ERROR) { if (rc == SSH_ERROR) {
goto fail; goto fail;
} }
explicit_bzero(ed25519_privkey, ED25519_KEY_LEN); ssh_burn(ed25519_privkey, ED25519_KEY_LEN);
SAFE_FREE(ed25519_privkey); SAFE_FREE(ed25519_privkey);
} else if (type == SSH_KEY_PRIVATE && } else if (type == SSH_KEY_PRIVATE &&
key->type == SSH_KEYTYPE_SK_ED25519) { key->type == SSH_KEYTYPE_SK_ED25519) {
@@ -2038,7 +2038,7 @@ fail:
#endif /* OPENSSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
free(ed25519_pubkey); free(ed25519_pubkey);
if (ed25519_privkey) { if (ed25519_privkey) {
explicit_bzero(ed25519_privkey, ED25519_KEY_LEN); ssh_burn(ed25519_privkey, ED25519_KEY_LEN);
free(ed25519_privkey); free(ed25519_privkey);
} }
@@ -2231,7 +2231,7 @@ static int pki_signature_from_rsa_blob(const ssh_key pubkey,
} }
/* front-pad the buffer with zeroes */ /* front-pad the buffer with zeroes */
explicit_bzero(blob_padded_data, pad_len); ssh_burn(blob_padded_data, pad_len);
/* fill the rest with the actual signature blob */ /* fill the rest with the actual signature blob */
memcpy(blob_padded_data + pad_len, blob_orig, len); memcpy(blob_padded_data + pad_len, blob_orig, len);
@@ -2360,17 +2360,17 @@ static int pki_signature_from_ecdsa_blob(UNUSED_PARAM(const ssh_key pubkey),
sig->raw_sig = ssh_string_new(raw_sig_len); sig->raw_sig = ssh_string_new(raw_sig_len);
if (sig->raw_sig == NULL) { if (sig->raw_sig == NULL) {
explicit_bzero(raw_sig_data, raw_sig_len); ssh_burn(raw_sig_data, raw_sig_len);
goto error; goto error;
} }
rc = ssh_string_fill(sig->raw_sig, raw_sig_data, raw_sig_len); rc = ssh_string_fill(sig->raw_sig, raw_sig_data, raw_sig_len);
if (rc < 0) { if (rc < 0) {
explicit_bzero(raw_sig_data, raw_sig_len); ssh_burn(raw_sig_data, raw_sig_len);
goto error; goto error;
} }
explicit_bzero(raw_sig_data, raw_sig_len); ssh_burn(raw_sig_data, raw_sig_len);
SAFE_FREE(raw_sig_data); SAFE_FREE(raw_sig_data);
ECDSA_SIG_free(ecdsa_sig); ECDSA_SIG_free(ecdsa_sig);
return SSH_OK; return SSH_OK;
@@ -2649,7 +2649,7 @@ out:
EVP_MD_CTX_free(ctx); EVP_MD_CTX_free(ctx);
} }
if (raw_sig_data != NULL) { if (raw_sig_data != NULL) {
explicit_bzero(raw_sig_data, raw_sig_len); ssh_burn(raw_sig_data, raw_sig_len);
} }
SAFE_FREE(raw_sig_data); SAFE_FREE(raw_sig_data);
EVP_PKEY_free(pkey); EVP_PKEY_free(pkey);

View File

@@ -331,8 +331,8 @@ int pki_ed25519_verify(const ssh_key pubkey,
hlen + ED25519_SIG_LEN, hlen + ED25519_SIG_LEN,
*pubkey->ed25519_pubkey); *pubkey->ed25519_pubkey);
explicit_bzero(buffer, hlen + ED25519_SIG_LEN); ssh_burn(buffer, hlen + ED25519_SIG_LEN);
explicit_bzero(buffer2, hlen); ssh_burn(buffer2, hlen);
SAFE_FREE(buffer); SAFE_FREE(buffer);
SAFE_FREE(buffer2); SAFE_FREE(buffer2);
if (rc == 0) { if (rc == 0) {

View File

@@ -1297,7 +1297,7 @@ static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey, const
blob_padded_data = (char *) ssh_string_data(sig_blob_padded); blob_padded_data = (char *) ssh_string_data(sig_blob_padded);
blob_orig = (char *) ssh_string_data(sig_blob); blob_orig = (char *) ssh_string_data(sig_blob);
explicit_bzero(blob_padded_data, pad_len); ssh_burn(blob_padded_data, pad_len);
memcpy(blob_padded_data + pad_len, blob_orig, len); memcpy(blob_padded_data + pad_len, blob_orig, len);
sig->rsa_sig = sig_blob_padded; sig->rsa_sig = sig_blob_padded;
@@ -1486,7 +1486,7 @@ static ssh_string rsa_do_sign_hash(const unsigned char *digest,
} }
ok = ssh_string_fill(sig_blob, sig, slen); ok = ssh_string_fill(sig_blob, sig, slen);
explicit_bzero(sig, slen); ssh_burn(sig, slen);
SAFE_FREE(sig); SAFE_FREE(sig);
if (ok < 0) { if (ok < 0) {
SSH_STRING_FREE(sig_blob); SSH_STRING_FREE(sig_blob);

View File

@@ -350,7 +350,7 @@ int pki_sk_enroll_key(ssh_pki_ctx context,
pin_to_use = pin_buf; pin_to_use = pin_buf;
} else { } else {
SSH_LOG(SSH_LOG_WARN, "Failed to fetch PIN from callback"); SSH_LOG(SSH_LOG_WARN, "Failed to fetch PIN from callback");
explicit_bzero(pin_buf, sizeof(pin_buf)); ssh_burn(pin_buf, sizeof(pin_buf));
goto out; goto out;
} }
} else { } else {
@@ -365,7 +365,7 @@ int pki_sk_enroll_key(ssh_pki_ctx context,
pin_to_use, pin_to_use,
context->sk_callbacks_options, context->sk_callbacks_options,
&enroll_response); &enroll_response);
explicit_bzero(pin_buf, sizeof(pin_buf)); ssh_burn(pin_buf, sizeof(pin_buf));
if (rc != SSH_OK) { if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARN, SSH_LOG(SSH_LOG_WARN,
"Security key enroll callback failed: %s (%d)", "Security key enroll callback failed: %s (%d)",
@@ -407,7 +407,7 @@ int pki_sk_enroll_key(ssh_pki_ctx context,
out: out:
if (challenge == random_challenge) { if (challenge == random_challenge) {
explicit_bzero(random_challenge, sizeof(random_challenge)); ssh_burn(random_challenge, sizeof(random_challenge));
} }
SK_ENROLL_RESPONSE_FREE(enroll_response); SK_ENROLL_RESPONSE_FREE(enroll_response);
@@ -697,7 +697,7 @@ ssh_signature pki_sk_do_sign(ssh_pki_ctx context,
pin_to_use = pin_buf; pin_to_use = pin_buf;
} else { } else {
SSH_LOG(SSH_LOG_WARN, "Failed to fetch PIN from callback"); SSH_LOG(SSH_LOG_WARN, "Failed to fetch PIN from callback");
explicit_bzero(pin_buf, sizeof(pin_buf)); ssh_burn(pin_buf, sizeof(pin_buf));
goto error; goto error;
} }
} else { } else {
@@ -714,7 +714,7 @@ ssh_signature pki_sk_do_sign(ssh_pki_ctx context,
pin_to_use, pin_to_use,
context->sk_callbacks_options, context->sk_callbacks_options,
&sign_response); &sign_response);
explicit_bzero(pin_buf, sizeof(pin_buf)); ssh_burn(pin_buf, sizeof(pin_buf));
if (rc != SSH_OK) { if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARN, SSH_LOG(SSH_LOG_WARN,
"Security key sign callback failed: %s (%d)", "Security key sign callback failed: %s (%d)",
@@ -833,7 +833,7 @@ int ssh_sk_resident_keys_load(const struct ssh_pki_ctx_struct *pki_context,
pin_to_use = pin_buf; pin_to_use = pin_buf;
} else { } else {
SSH_LOG(SSH_LOG_WARN, "Failed to fetch PIN from callback"); SSH_LOG(SSH_LOG_WARN, "Failed to fetch PIN from callback");
explicit_bzero(pin_buf, sizeof(pin_buf)); ssh_burn(pin_buf, sizeof(pin_buf));
goto out; goto out;
} }
} else { } else {
@@ -844,7 +844,7 @@ int ssh_sk_resident_keys_load(const struct ssh_pki_ctx_struct *pki_context,
ctx_to_use->sk_callbacks_options, ctx_to_use->sk_callbacks_options,
&raw_resident_keys, &raw_resident_keys,
&raw_keys_count); &raw_keys_count);
explicit_bzero(pin_buf, sizeof(pin_buf)); ssh_burn(pin_buf, sizeof(pin_buf));
if (rc != SSH_OK) { if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARN, SSH_LOG(SSH_LOG_WARN,
"Security key load_resident_keys callback failed: %s (%d)", "Security key load_resident_keys callback failed: %s (%d)",

View File

@@ -497,6 +497,11 @@ static size_t callback_receive_banner(const void *data, size_t len, void *user)
buffer[i] = '\0'; buffer[i] = '\0';
str = strdup(buffer); str = strdup(buffer);
if (str == NULL) {
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_set_error_oom(session);
return 0;
}
/* number of bytes read */ /* number of bytes read */
processed = i + 1; processed = i + 1;
session->clientbanner = str; session->clientbanner = str;

View File

@@ -417,7 +417,7 @@ void ssh_free(ssh_session session)
_ssh_remove_legacy_log_cb(); _ssh_remove_legacy_log_cb();
/* burn connection, it could contain sensitive data */ /* burn connection, it could contain sensitive data */
explicit_bzero(session, sizeof(struct ssh_session_struct)); ssh_burn(session, sizeof(struct ssh_session_struct));
SAFE_FREE(session); SAFE_FREE(session);
} }

View File

@@ -62,7 +62,7 @@ void sk_enroll_response_burn(struct sk_enroll_response *enroll_response)
enroll_response->attestation_cert_len); enroll_response->attestation_cert_len);
BURN_FREE(enroll_response->authdata, enroll_response->authdata_len); BURN_FREE(enroll_response->authdata, enroll_response->authdata_len);
explicit_bzero(enroll_response, sizeof(*enroll_response)); ssh_burn(enroll_response, sizeof(*enroll_response));
} }
void sk_enroll_response_free(struct sk_enroll_response *enroll_response) void sk_enroll_response_free(struct sk_enroll_response *enroll_response)

View File

@@ -153,7 +153,7 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
rc = ssh_curve25519_create_k(session, k); rc = ssh_curve25519_create_k(session, k);
if (rc != SSH_OK) { if (rc != SSH_OK) {
return SSH_ERROR; goto cleanup;
} }
#ifdef DEBUG_CRYPTO #ifdef DEBUG_CRYPTO
@@ -176,7 +176,8 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
SSH_LOG(SSH_LOG_TRACE, SSH_LOG(SSH_LOG_TRACE,
"Failed to encapsulate sntrup761 shared secret: %s", "Failed to encapsulate sntrup761 shared secret: %s",
gpg_strerror(err)); gpg_strerror(err));
return SSH_ERROR; rc = SSH_ERROR;
goto cleanup;
} }
} else { } else {
gcry_error_t err; gcry_error_t err;
@@ -193,7 +194,8 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
SSH_LOG(SSH_LOG_TRACE, SSH_LOG(SSH_LOG_TRACE,
"Failed to decapsulate sntrup761 shared secret: %s", "Failed to decapsulate sntrup761 shared secret: %s",
gpg_strerror(err)); gpg_strerror(err));
return SSH_ERROR; rc = SSH_ERROR;
goto cleanup;
} }
} }
#else #else
@@ -204,7 +206,8 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
&rc, &rc,
crypto_random); crypto_random);
if (rc != 1) { if (rc != 1) {
return SSH_ERROR; rc = SSH_ERROR;
goto cleanup;
} }
} else { } else {
sntrup761_dec(ssk, sntrup761_dec(ssk,
@@ -224,7 +227,8 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
bignum_bin2bn(hss, sizeof hss, &session->next_crypto->shared_secret); bignum_bin2bn(hss, sizeof hss, &session->next_crypto->shared_secret);
if (session->next_crypto->shared_secret == NULL) { if (session->next_crypto->shared_secret == NULL) {
return SSH_ERROR; rc = SSH_ERROR;
goto cleanup;
} }
#ifdef DEBUG_CRYPTO #ifdef DEBUG_CRYPTO
@@ -232,6 +236,11 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
#endif #endif
return 0; return 0;
cleanup:
ssh_burn(ssk, sizeof ssk);
ssh_burn(hss, sizeof hss);
return rc;
} }
/** @internal /** @internal

View File

@@ -344,7 +344,7 @@ void ssh_string_burn(struct ssh_string_struct *s)
return; return;
} }
explicit_bzero(s->data, ssh_string_len(s)); ssh_burn(s->data, ssh_string_len(s));
} }
/** /**

View File

@@ -48,7 +48,7 @@ void ssh_tokens_free(struct ssh_tokens_st *tokens)
if (tokens->tokens != NULL) { if (tokens->tokens != NULL) {
for (i = 0; tokens->tokens[i] != NULL; i++) { for (i = 0; tokens->tokens[i] != NULL; i++) {
explicit_bzero(tokens->tokens[i], strlen(tokens->tokens[i])); ssh_burn(tokens->tokens[i], strlen(tokens->tokens[i]));
} }
} }

View File

@@ -199,11 +199,11 @@ void crypto_free(struct ssh_crypto_struct *crypto)
#endif #endif
SAFE_FREE(crypto->dh_server_signature); SAFE_FREE(crypto->dh_server_signature);
if (crypto->session_id != NULL) { if (crypto->session_id != NULL) {
explicit_bzero(crypto->session_id, crypto->session_id_len); ssh_burn(crypto->session_id, crypto->session_id_len);
SAFE_FREE(crypto->session_id); SAFE_FREE(crypto->session_id);
} }
if (crypto->secret_hash != NULL) { if (crypto->secret_hash != NULL) {
explicit_bzero(crypto->secret_hash, crypto->digest_len); ssh_burn(crypto->secret_hash, crypto->digest_len);
SAFE_FREE(crypto->secret_hash); SAFE_FREE(crypto->secret_hash);
} }
compress_cleanup(crypto); compress_cleanup(crypto);
@@ -212,11 +212,11 @@ void crypto_free(struct ssh_crypto_struct *crypto)
SAFE_FREE(crypto->encryptMAC); SAFE_FREE(crypto->encryptMAC);
SAFE_FREE(crypto->decryptMAC); SAFE_FREE(crypto->decryptMAC);
if (crypto->encryptkey != NULL) { if (crypto->encryptkey != NULL) {
explicit_bzero(crypto->encryptkey, crypto->out_cipher->keysize / 8); ssh_burn(crypto->encryptkey, crypto->out_cipher->keysize / 8);
SAFE_FREE(crypto->encryptkey); SAFE_FREE(crypto->encryptkey);
} }
if (crypto->decryptkey != NULL) { if (crypto->decryptkey != NULL) {
explicit_bzero(crypto->decryptkey, crypto->in_cipher->keysize / 8); ssh_burn(crypto->decryptkey, crypto->in_cipher->keysize / 8);
SAFE_FREE(crypto->decryptkey); SAFE_FREE(crypto->decryptkey);
} }
@@ -239,7 +239,7 @@ void crypto_free(struct ssh_crypto_struct *crypto)
ssh_string_free(crypto->hybrid_shared_secret); ssh_string_free(crypto->hybrid_shared_secret);
#endif #endif
explicit_bzero(crypto, sizeof(struct ssh_crypto_struct)); ssh_burn(crypto, sizeof(struct ssh_crypto_struct));
SAFE_FREE(crypto); SAFE_FREE(crypto);
} }

View File

@@ -11,6 +11,7 @@
#include "torture.h" #include "torture.h"
#include "torture_key.h" #include "torture_key.h"
#include <errno.h> #include <errno.h>
#include <libssh/config.h>
#include <libssh/misc.h> #include <libssh/misc.h>
#include <libssh/options.h> #include <libssh/options.h>
#include <libssh/pki.h> #include <libssh/pki.h>
@@ -1615,6 +1616,108 @@ static void torture_options_getopt(void **state)
#endif /* _NSC_VER */ #endif /* _NSC_VER */
} }
static void torture_options_getopt_o_option(void **state)
{
#ifndef _MSC_VER
ssh_session session = *state;
int rc;
enum ssh_config_opcode_e opcode =
ssh_config_get_opcode((char *)"compression");
const char *argv[6] = {EXECUTABLE_NAME, "-o", "Compression nah", NULL};
int argc = 3;
// Test: -o with invalid value (e.g., "-o Compression nah")
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
session->opts.options_seen[opcode] = 0;
// Test: -o with valid value (e.g., "-o Compression yes")
argv[1] = "-o";
argv[2] = "compression yes";
argv[3] = NULL;
argc = 3;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code(session, rc);
assert_int_equal(session->opts.options_seen[opcode], 1);
#ifdef WITH_ZLIB
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S],
"zlib@openssh.com,none");
assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C],
"zlib@openssh.com,none");
#else
assert_string_equal(session->opts.wanted_methods[SSH_COMP_C_S], "none");
assert_string_equal(session->opts.wanted_methods[SSH_COMP_S_C], "none");
#endif
// Test: -o with missing value (e.g., "-o =")
argv[1] = "-o";
argv[2] = "=";
argv[3] = NULL;
argc = 3;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code(session, rc);
// Test: -o with only option name, no value (e.g., "-o Compression")
session->opts.options_seen[opcode] = 0;
argv[1] = "-o";
argv[2] = "Compression";
argv[3] = NULL;
argc = 3;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
// Test: -o with empty string (e.g., "-o ")
argv[1] = "-o";
argv[2] = "";
argv[3] = NULL;
argc = 3;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
// Test: -o with unsupported option on the cli
argv[1] = "-o";
argv[2] = "match *";
argv[3] = NULL;
argc = 3;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
// Test: multiple -o options together, one invalid
session->opts.options_seen[opcode] = 0;
argv[1] = "-o";
argv[2] = "compression yes";
argv[3] = "-o";
argv[4] = "enablesshkeysign yes";
argv[5] = NULL;
argc = 5;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
// Test: multiple -o options together, all valid
session->opts.options_seen[opcode] = 0;
argv[1] = "-o";
argv[2] = "compression no";
argv[3] = "-o";
argv[4] = "rekeylimit 1G 1h";
argv[5] = NULL;
argc = 5;
rc = ssh_options_getopt(session, &argc, (char **)argv);
assert_ssh_return_code(session, rc);
opcode = ssh_config_get_opcode((char *)"compression");
assert_int_equal(session->opts.options_seen[opcode], 1);
opcode = ssh_config_get_opcode((char *)"rekeylimit");
assert_int_equal(session->opts.options_seen[opcode], 1);
#endif /* _MSC_VER */
}
static void torture_options_plus_sign(void **state) static void torture_options_plus_sign(void **state)
{ {
ssh_session session = *state; ssh_session session = *state;
@@ -3026,6 +3129,9 @@ torture_run_tests(void)
cmocka_unit_test_setup_teardown(torture_options_getopt, cmocka_unit_test_setup_teardown(torture_options_getopt,
setup, setup,
teardown), teardown),
cmocka_unit_test_setup_teardown(torture_options_getopt_o_option,
setup,
teardown),
cmocka_unit_test_setup_teardown(torture_options_plus_sign, cmocka_unit_test_setup_teardown(torture_options_plus_sign,
setup, setup,
teardown), teardown),

View File

@@ -129,7 +129,7 @@ torture_packet(const char *cipher, const char *mac_type,
assert_int_equal(rc, encrypted_packet_len); assert_int_equal(rc, encrypted_packet_len);
ssh_packet_set_callbacks(session, &cb); ssh_packet_set_callbacks(session, &cb);
explicit_bzero(response, sizeof(response)); ssh_burn(response, sizeof(response));
rc = ssh_packet_socket_callback(buffer, encrypted_packet_len, session); rc = ssh_packet_socket_callback(buffer, encrypted_packet_len, session);
assert_int_not_equal(rc, SSH_ERROR); assert_int_not_equal(rc, SSH_ERROR);
if(payload_len > 0){ if(payload_len > 0){