Allow building without the exec() supported ...

.. to satisfy restricted environment or fuzzers

We are encountering weird issues in the oss-fuzz that the file disappears during
coverage build so I assume some corpus sneaked in, that contains some commands
that end up being executed as part of the coverage run causing it randomly
failing.

The solution I propose is to build fuzzers without ability to call arbitrary
commands on the filesystem (such as `rm -rf /`) as this is not the point the
fuzzers should be testing.

This is controlled by the WITH_EXEC CMake option (enabled by default).

https://github.com/google/oss-fuzz/issues/10136

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Sahana Prasad <sahana@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
This commit is contained in:
Jakub Jelen
2024-07-04 18:28:43 +02:00
committed by Sahana Prasad
parent 2fe9ed1764
commit bed4438695
12 changed files with 81 additions and 34 deletions

View File

@@ -69,7 +69,9 @@ static void torture_options_set_proxycommand(void **state)
char command[255] = {0};
struct stat sb;
int rc;
#ifdef WITH_EXEC
socket_t fd;
#endif
rc = stat(NCAT_EXECUTABLE, &sb);
if (rc != 0 || (sb.st_mode & S_IXOTH) == 0) {
@@ -88,11 +90,15 @@ static void torture_options_set_proxycommand(void **state)
rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command);
assert_int_equal(rc, 0);
rc = ssh_connect(session);
#ifdef WITH_EXEC
assert_ssh_return_code(session, rc);
fd = ssh_get_fd(session);
assert_true(fd != SSH_INVALID_SOCKET);
rc = fcntl(fd, F_GETFL);
assert_int_equal(rc & O_RDWR, O_RDWR);
#else
assert_int_equal(rc, SSH_ERROR);
#endif /* WITH_EXEC */
}
#else /* NCAT_EXECUTABLE */
@@ -124,7 +130,9 @@ static void torture_options_set_proxycommand_ssh(void **state)
const char *address = torture_server_address(AF_INET);
char command[255] = {0};
int rc;
#ifdef WITH_EXEC
socket_t fd;
#endif
rc = snprintf(command, sizeof(command),
"ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -W [%%h]:%%p alice@%s",
@@ -134,11 +142,15 @@ static void torture_options_set_proxycommand_ssh(void **state)
rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command);
assert_int_equal(rc, 0);
rc = ssh_connect(session);
#ifdef WITH_EXEC
assert_ssh_return_code(session, rc);
fd = ssh_get_fd(session);
assert_true(fd != SSH_INVALID_SOCKET);
rc = fcntl(fd, F_GETFL);
assert_int_equal(rc & O_RDWR, O_RDWR);
#else
assert_int_equal(rc, SSH_ERROR);
#endif /* WITH_EXEC */
}
static void torture_options_set_proxycommand_ssh_stderr(void **state)
@@ -148,7 +160,9 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state)
const char *address = torture_server_address(AF_INET);
char command[255] = {0};
int rc;
#ifdef WITH_EXEC
socket_t fd;
#endif
/* The -vvv switches produce the desired output on the standard error */
rc = snprintf(command, sizeof(command),
@@ -159,11 +173,15 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state)
rc = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, command);
assert_int_equal(rc, 0);
rc = ssh_connect(session);
#ifdef WITH_EXEC
assert_ssh_return_code(session, rc);
fd = ssh_get_fd(session);
assert_true(fd != SSH_INVALID_SOCKET);
rc = fcntl(fd, F_GETFL);
assert_int_equal(rc & O_RDWR, O_RDWR);
#else
assert_int_equal(rc, SSH_ERROR);
#endif /* WITH_EXEC */
}
static void torture_options_proxycommand_injection(void **state)

View File

@@ -10,6 +10,8 @@ but they are suitable for debugging.
## Background
### Turn off encryption
Fuzzing ssh protocol is complicated by the way that all the communication
between client and server is encrypted and authenticated using keys based
on random data, making it impossible to fuzz the actual underlying protocol
@@ -17,6 +19,17 @@ as every change in the encrypted data causes integrity errors. For that reason,
libssh needs to implement "none" cipher and MAC as described in RFC 4253
and these need to be used during fuzzing to be able to accomplish
reproducibility and for fuzzers to be able to progress behind key exchange.
This is enabled with the `WITH_INSECURE_NONE` CMake option.
### Do not allow filesystem modification
The OpenSSH configuration files are quite rich and expects users to know what
they do when they write their configuration files. The fuzzer driver is not an
average user so it is very happy to try whatever commands come to its "mind",
including `rm -rf /` and libssh would be very happy to run it by default. This
might remove some parts of the system that are mandatory for fuzzing.
To avoid executing dangerous commands like this, the `WITH_EXEC=OFF` CMake
option prevents invoking any external command through `exec()` syscall.
## Corpus creation
@@ -31,7 +44,7 @@ to use none cipher for the key exchange to be plausible.
* Compile libssh with support for none cipher and pcap:
cmake -DWITH_INSECURE_NONE=ON -DWITH_PCAP=ON ../
cmake -DWITH_INSECURE_NONE=ON -DWITH_EXEC=OFF -DWITH_PCAP=ON ../
* Create a configuration file enabling none cipher and mac:

View File

@@ -862,7 +862,7 @@ static void torture_config_match(void **state,
}
torture_reset_config(session);
_parse_config(session, file, string, SSH_OK);
#ifdef _WIN32
#ifndef WITH_EXEC
/* The match exec is not supported on windows at this moment */
assert_string_equal(session->opts.host, "otherhost");
#else
@@ -878,7 +878,7 @@ static void torture_config_match(void **state,
}
torture_reset_config(session);
_parse_config(session, file, string, SSH_OK);
#ifdef _WIN32
#ifndef WITH_EXEC
/* The match exec is not supported on windows at this moment */
assert_string_equal(session->opts.host, "otherhost");
#else
@@ -894,7 +894,7 @@ static void torture_config_match(void **state,
}
torture_reset_config(session);
_parse_config(session, file, string, SSH_OK);
#ifdef _WIN32
#ifndef WITH_EXEC
/* The match exec is not supported on windows at this moment */
assert_string_equal(session->opts.host, "otherhost");
#else
@@ -1855,7 +1855,7 @@ static void torture_config_parser_get_cmd(void **state)
{
char *p = NULL, *tok = NULL;
char data[256];
#ifdef __unix__
#ifdef WITH_EXEC
FILE *outfile = NULL, *infile = NULL;
int pid;
char buffer[256] = {0};
@@ -1899,7 +1899,7 @@ static void torture_config_parser_get_cmd(void **state)
assert_string_equal(tok, data);
assert_int_equal(*p, '\0');
#ifdef __unix__
#ifdef WITH_EXEC
/* Check if the command would get correctly executed
* Use the script file "hello world.sh" to echo the first argument
* Run as <= "/workdir/hello world.sh" "hello libssh" => */
@@ -1929,7 +1929,7 @@ static void torture_config_parser_get_cmd(void **state)
fclose(outfile);
assert_string_equal(buffer, "hello libssh");
#endif
#endif /* WITH_EXEC */
}
/* ssh_config_get_token() should behave as expected

View File

@@ -1147,7 +1147,7 @@ static void torture_options_config_match(void **state)
rv = ssh_options_parse_config(session, "test_config");
assert_ssh_return_code(session, rv);
#ifdef _WIN32
#ifndef WITH_EXEC
/* The match exec is not supported on windows at this moment */
assert_int_equal(session->opts.port, 34);
#else
@@ -1169,7 +1169,7 @@ static void torture_options_config_match(void **state)
rv = ssh_options_parse_config(session, "test_config");
assert_ssh_return_code(session, rv);
#ifdef _WIN32
#ifndef WITH_EXEC
/* The match exec is not supported on windows at this moment */
assert_int_equal(session->opts.port, 34);
#else
@@ -1222,7 +1222,7 @@ static void torture_options_config_match_multi(void **state)
rv = ssh_options_parse_config(session, "test_config");
assert_ssh_return_code(session, rv);
#ifdef _WIN32
#ifndef WITH_EXEC
/* The match exec is not supported on windows at this moment */
assert_int_equal(session->opts.port, 34);
#else