1
1

CVE-2018-10933: Add tests for packet filtering

Created the test torture_packet_filter.c which tests if packets are
being correctly filtered.

Fixes T101

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
Anderson Toshiyuki Sasaki 2018-09-07 15:26:49 +02:00 коммит произвёл Andreas Schneider
родитель 75be012b4a
Коммит e1548a71bd
2 изменённых файлов: 503 добавлений и 0 удалений

Просмотреть файл

@ -15,6 +15,7 @@ set(LIBSSH_UNIT_TESTS
torture_isipaddr
torture_knownhosts_parsing
torture_hashes
torture_packet_filter
)
set(LIBSSH_THREAD_UNIT_TESTS

502
tests/unittests/torture_packet_filter.c Обычный файл
Просмотреть файл

@ -0,0 +1,502 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2018 by Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
/*
* This test checks if the messages accepted by the packet filter were intented
* to be accepted.
*
* The process consists in 2 steps:
* - Try the filter with a message type in an arbitrary state
* - If the message is accepted by the filter, check if the message is in the
* set of accepted states.
*
* Only the values selected by the flag (COMPARE_*) are considered.
* */
#include "config.h"
#define LIBSSH_STATIC
#include "torture.h"
#include "libssh/priv.h"
#include "libssh/libssh.h"
#include "libssh/session.h"
#include "libssh/auth.h"
#include "libssh/ssh2.h"
#include "libssh/packet.h"
#include "packet.c"
#define COMPARE_SESSION_STATE 1
#define COMPARE_ROLE (1 << 1)
#define COMPARE_DH_STATE (1 << 2)
#define COMPARE_AUTH_STATE (1 << 3)
#define COMPARE_GLOBAL_REQ_STATE (1 << 4)
#define COMPARE_CURRENT_METHOD (1 << 5)
#define SESSION_STATE_COUNT 11
#define DH_STATE_COUNT 4
#define AUTH_STATE_COUNT 15
#define GLOBAL_REQ_STATE_COUNT 5
#define MESSAGE_COUNT 100 // from 1 to 100
#define ROLE_CLIENT 0
#define ROLE_SERVER 1
/*
* This is the list of currently unfiltered message types.
* Only unrecognized types should be in this list.
* */
static uint8_t unfiltered[] = {
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
22, 23, 24, 25, 26, 27, 28, 29,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
54, 55, 56, 57, 58, 59,
62,
67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
83, 84, 85, 86, 87, 88, 89,
};
typedef struct global_state_st {
/* If the bit in this flag is zero, the corresponding state is not
* considered, working as a wildcard (meaning any value is accepted) */
uint32_t flags;
uint8_t role;
enum ssh_session_state_e session;
enum ssh_dh_state_e dh;
enum ssh_auth_state_e auth;
enum ssh_channel_request_state_e global_req;
} global_state;
static int cmp_state(const void *e1, const void *e2)
{
global_state *s1 = (global_state *) e1;
global_state *s2 = (global_state *) e2;
/* Compare role (client == 0 or server == 1)*/
if (s1->role < s2->role) {
return -1;
}
else if (s1->role > s2->role) {
return 1;
}
/* Compare session state */
if (s1->session < s2->session) {
return -1;
}
else if (s1->session > s2->session) {
return 1;
}
/* Compare DH state */
if (s1->dh < s2->dh) {
return -1;
}
else if (s1->dh > s2->dh) {
return 1;
}
/* Compare auth */
if (s1->auth < s2->auth) {
return -1;
}
else if (s1->auth > s2->auth) {
return 1;
}
/* Compare global_req */
if (s1->global_req < s2->global_req) {
return -1;
}
else if (s1->global_req > s2->global_req) {
return 1;
}
/* If all equal, they are equal */
return 0;
}
static int cmp_state_search(const void *key, const void *array_element)
{
global_state *s1 = (global_state *) key;
global_state *s2 = (global_state *) array_element;
int result = 0;
if (s2->flags & COMPARE_ROLE) {
/* Compare role (client == 0 or server == 1)*/
if (s1->role < s2->role) {
return -1;
}
else if (s1->role > s2->role) {
return 1;
}
}
if (s2->flags & COMPARE_SESSION_STATE) {
/* Compare session state */
if (s1->session < s2->session) {
result = -1;
goto end;
}
else if (s1->session > s2->session) {
result = 1;
goto end;
}
}
if (s2->flags & COMPARE_DH_STATE) {
/* Compare DH state */
if (s1->dh < s2->dh) {
result = -1;
goto end;
}
else if (s1->dh > s2->dh) {
result = 1;
goto end;
}
}
if (s2->flags & COMPARE_AUTH_STATE) {
/* Compare auth */
if (s1->auth < s2->auth) {
result = -1;
goto end;
}
else if (s1->auth > s2->auth) {
result = 1;
goto end;
}
}
if (s2->flags & COMPARE_GLOBAL_REQ_STATE) {
/* Compare global_req */
if (s1->global_req < s2->global_req) {
result = -1;
goto end;
}
else if (s1->global_req > s2->global_req) {
result = 1;
goto end;
}
}
end:
return result;
}
static int is_state_accepted(global_state *tested, global_state *accepted,
int accepted_len)
{
global_state *found = NULL;
found = bsearch(tested, accepted, accepted_len, sizeof(global_state),
cmp_state_search);
if (found != NULL) {
return 1;
}
return 0;
}
static int cmp_uint8(const void *i, const void *j)
{
uint8_t e1 = *((uint8_t *)i);
uint8_t e2 = *((uint8_t *)j);
if (e1 < e2) {
return -1;
}
else if (e1 > e2) {
return 1;
}
return 0;
}
static int check_unfiltered(uint8_t msg_type)
{
uint8_t *found;
found = bsearch(&msg_type, unfiltered, sizeof(unfiltered)/sizeof(uint8_t),
sizeof(uint8_t), cmp_uint8);
if (found != NULL) {
return 1;
}
return 0;
}
static void torture_packet_filter_check_unfiltered(void **state)
{
ssh_session session;
int role_c;
int auth_c;
int session_c;
int dh_c;
int global_req_c;
uint8_t msg_type;
enum ssh_packet_filter_result_e rc;
int in_unfiltered;
session = ssh_new();
for (msg_type = 1; msg_type <= MESSAGE_COUNT; msg_type++) {
session->in_packet.type = msg_type;
for (role_c = 0; role_c < 2; role_c++) {
session->server = role_c;
for (session_c = 0; session_c < SESSION_STATE_COUNT; session_c++) {
session->session_state = session_c;
for (dh_c = 0; dh_c < DH_STATE_COUNT; dh_c++) {
session->dh_handshake_state = dh_c;
for (auth_c = 0; auth_c < AUTH_STATE_COUNT; auth_c++) {
session->auth.state = auth_c;
for (global_req_c = 0;
global_req_c < GLOBAL_REQ_STATE_COUNT;
global_req_c++)
{
session->global_req_state = global_req_c;
rc = ssh_packet_incoming_filter(session);
if (rc == SSH_PACKET_UNKNOWN) {
in_unfiltered = check_unfiltered(msg_type);
if (!in_unfiltered) {
fprintf(stderr, "Message type %d UNFILTERED "
"in state: role %d, session %d, dh %d, auth %d\n",
msg_type, role_c, session_c, dh_c, auth_c);
}
assert_int_equal(in_unfiltered, 1);
}
else {
in_unfiltered = check_unfiltered(msg_type);
if (in_unfiltered) {
fprintf(stderr, "Message type %d NOT UNFILTERED "
"in state: role %d, session %d, dh %d, auth %d\n",
msg_type, role_c, session_c, dh_c, auth_c);
}
assert_int_equal(in_unfiltered, 0);
}
}
}
}
}
}
}
ssh_free(session);
}
static int check_message_in_all_states(global_state accepted[],
int accepted_count, uint8_t msg_type)
{
ssh_session session;
int role_c;
int auth_c;
int session_c;
int dh_c;
int global_req_c;
enum ssh_packet_filter_result_e rc;
int in_accepted;
global_state key;
session = ssh_new();
/* Sort the accepted array so that the elements can be searched using
* bsearch */
qsort(accepted, accepted_count, sizeof(global_state), cmp_state);
session->in_packet.type = msg_type;
for (role_c = 0; role_c < 2; role_c++) {
session->server = role_c;
key.role = role_c;
for (session_c = 0; session_c < SESSION_STATE_COUNT; session_c++) {
session->session_state = session_c;
key.session = session_c;
for (dh_c = 0; dh_c < DH_STATE_COUNT; dh_c++) {
session->dh_handshake_state = dh_c;
key.dh = dh_c;
for (auth_c = 0; auth_c < AUTH_STATE_COUNT; auth_c++) {
session->auth.state = auth_c;
key.auth = auth_c;
for (global_req_c = 0;
global_req_c < GLOBAL_REQ_STATE_COUNT;
global_req_c++)
{
session->global_req_state = global_req_c;
key.global_req = global_req_c;
rc = ssh_packet_incoming_filter(session);
if (rc == SSH_PACKET_ALLOWED) {
in_accepted = is_state_accepted(&key, accepted,
accepted_count);
if (!in_accepted) {
fprintf(stderr, "Message type %d ALLOWED "
"in state: role %d, session %d, dh %d, auth %d\n",
msg_type, role_c, session_c, dh_c, auth_c);
}
assert_int_equal(in_accepted, 1);
}
else if (rc == SSH_PACKET_DENIED) {
in_accepted = is_state_accepted(&key, accepted, accepted_count);
if (in_accepted) {
fprintf(stderr, "Message type %d DENIED "
"in state: role %d, session %d, dh %d, auth %d\n",
msg_type, role_c, session_c, dh_c, auth_c);
}
assert_int_equal(in_accepted, 0);
}
else {
fprintf(stderr, "Message type %d UNFILTERED "
"in state: role %d, session %d, dh %d, auth %d\n",
msg_type, role_c, session_c, dh_c, auth_c);
}
}
}
}
}
}
ssh_free(session);
return 0;
}
static void torture_packet_filter_check_auth_success(void **state)
{
int rc;
global_state accepted[] = {
{
.flags = (COMPARE_SESSION_STATE |
COMPARE_ROLE |
COMPARE_AUTH_STATE |
COMPARE_DH_STATE),
.role = ROLE_CLIENT,
.session = SSH_SESSION_STATE_AUTHENTICATING,
.dh = DH_STATE_FINISHED,
.auth = SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
},
{
.flags = (COMPARE_SESSION_STATE |
COMPARE_ROLE |
COMPARE_AUTH_STATE |
COMPARE_DH_STATE),
.role = ROLE_CLIENT,
.session = SSH_SESSION_STATE_AUTHENTICATING,
.dh = DH_STATE_FINISHED,
.auth = SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
},
{
.flags = (COMPARE_SESSION_STATE |
COMPARE_ROLE |
COMPARE_AUTH_STATE |
COMPARE_DH_STATE),
.role = ROLE_CLIENT,
.session = SSH_SESSION_STATE_AUTHENTICATING,
.dh = DH_STATE_FINISHED,
.auth = SSH_AUTH_STATE_GSSAPI_MIC_SENT,
},
{
.flags = (COMPARE_SESSION_STATE |
COMPARE_ROLE |
COMPARE_AUTH_STATE |
COMPARE_DH_STATE),
.role = ROLE_CLIENT,
.session = SSH_SESSION_STATE_AUTHENTICATING,
.dh = DH_STATE_FINISHED,
.auth = SSH_AUTH_STATE_KBDINT_SENT,
},
{
.flags = (COMPARE_SESSION_STATE |
COMPARE_ROLE |
COMPARE_AUTH_STATE |
COMPARE_DH_STATE |
COMPARE_CURRENT_METHOD),
.role = ROLE_CLIENT,
.session = SSH_SESSION_STATE_AUTHENTICATING,
.dh = DH_STATE_FINISHED,
.auth = SSH_AUTH_STATE_AUTH_NONE_SENT,
}
};
int accepted_count = 5;
/* Unused */
(void) state;
rc = check_message_in_all_states(accepted, accepted_count,
SSH2_MSG_USERAUTH_SUCCESS);
assert_int_equal(rc, 0);
}
static void torture_packet_filter_check_channel_open(void **state)
{
int rc;
/* The only condition to accept a CHANNEL_OPEN is to be authenticated */
global_state accepted[] = {
{
.flags = COMPARE_SESSION_STATE,
.session = SSH_SESSION_STATE_AUTHENTICATED,
}
};
int accepted_count = 1;
/* Unused */
(void) state;
rc = check_message_in_all_states(accepted, accepted_count,
SSH2_MSG_CHANNEL_OPEN);
assert_int_equal(rc, 0);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_packet_filter_check_auth_success),
cmocka_unit_test(torture_packet_filter_check_channel_open),
cmocka_unit_test(torture_packet_filter_check_unfiltered),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, NULL, NULL);
ssh_finalize();
return rc;
}