From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
Date: Wed, 19 Sep 2018 15:04:31 +0200
Subject: [7/8] CVE-2018-10933: Introduced packet filtering
Origin: https://git.libssh.org/projects/libssh.git/commit/?id=b9033ad56a498d0642f3258a54a32251278a319e
Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2018-10933
Bug-Debian: https://bugs.debian.org/911149

The packet filter checks required states for the incoming packets and
reject them if they arrived in the wrong state.

Fixes T101

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
 include/libssh/packet.h |   6 +
 src/packet.c            | 787 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 791 insertions(+), 2 deletions(-)

diff --git a/include/libssh/packet.h b/include/libssh/packet.h
index d8ef35bbf24b..206c0b21a49c 100644
--- a/include/libssh/packet.h
+++ b/include/libssh/packet.h
@@ -43,6 +43,12 @@ enum ssh_packet_state_e {
   PACKET_STATE_PROCESSING
 };
 
+enum ssh_packet_filter_result_e {
+    SSH_PACKET_UNKNOWN,
+    SSH_PACKET_ALLOWED,
+    SSH_PACKET_DENIED
+};
+
 int packet_send(ssh_session session);
 
 #ifdef WITH_SSH1
diff --git a/src/packet.c b/src/packet.c
index d16cd1653eed..5a263328f909 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -127,6 +127,775 @@ static ssh_packet_callback default_packet_handlers[]= {
   ssh_packet_channel_failure,              // SSH2_MSG_CHANNEL_FAILURE            100
 };
 
+/** @internal
+ * @brief check if the received packet is allowed for the current session state
+ * @param session current ssh_session
+ * @returns SSH_PACKET_ALLOWED if the packet is allowed; SSH_PACKET_DENIED
+ * if the packet arrived in wrong state; SSH_PACKET_UNKNOWN if the packet type
+ * is unknown
+ */
+static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session session)
+{
+    enum ssh_packet_filter_result_e rc;
+
+#ifdef DEBUG_PACKET
+    SSH_LOG(SSH_LOG_PACKET, "Filtering packet type %d",
+            session->in_packet.type);
+#endif
+
+    switch(session->in_packet.type) {
+    case SSH2_MSG_DISCONNECT:                         // 1
+        /*
+         * States required:
+         * - None
+         *
+         * Transitions:
+         * - session->socket->state = SSH_SOCKET_CLOSED
+         * - session->session_state = SSH_SESSION_STATE_ERROR
+         * */
+
+        /* Always allowed */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_IGNORE:                             // 2
+        /*
+         * States required:
+         * - None
+         *
+         * Transitions:
+         * - None
+         * */
+
+        /* Always allowed */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_UNIMPLEMENTED:                      // 3
+        /*
+         * States required:
+         * - None
+         *
+         * Transitions:
+         * - None
+         * */
+
+        /* Always allowed */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_DEBUG:                              // 4
+        /*
+         * States required:
+         * - None
+         *
+         * Transitions:
+         * - None
+         * */
+
+        /* Always allowed */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_SERVICE_REQUEST:                    // 5
+        /* Server only */
+
+        /*
+         * States required:
+         * - session->session_state == SSH_SESSION_STATE_AUTHENTICATING
+         *   or session->session_state == SSH_SESSION_STATE_AUTHENTICATED
+         * - session->dh_handshake_state == DH_STATE_FINISHED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        /* If this is a client, reject the message */
+        if (session->client) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if ((session->session_state != SSH_SESSION_STATE_AUTHENTICATING) &&
+            (session->session_state != SSH_SESSION_STATE_AUTHENTICATED))
+        {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_FINISHED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_SERVICE_ACCEPT:                     // 6
+        /*
+         * States required:
+         * - session->session_state == SSH_SESSION_STATE_AUTHENTICATING
+         *   or session->session_state == SSH_SESSION_STATE_AUTHENTICATED
+         * - session->dh_handshake_state == DH_STATE_FINISHED
+         * - session->auth_service_state == SSH_AUTH_SERVICE_SENT
+         *
+         * Transitions:
+         * - auth_service_state = SSH_AUTH_SERVICE_ACCEPTED
+         * */
+
+        if ((session->session_state != SSH_SESSION_STATE_AUTHENTICATING) &&
+            (session->session_state != SSH_SESSION_STATE_AUTHENTICATED))
+        {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_FINISHED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        /* TODO check if only auth service can be requested */
+        if (session->auth_service_state != SSH_AUTH_SERVICE_SENT) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_KEXINIT:                            // 20
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *   or session_state == SSH_SESSION_STATE_INITIAL_KEX
+         * - dh_handshake_state == DH_STATE_INIT
+         *   or dh_handshake_state == DH_STATE_FINISHED (re-exchange)
+         *
+         * Transitions:
+         * - session->dh_handshake_state = DH_STATE_INIT
+         * - session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED
+         *
+         * On server:
+         * - session->session_state = SSH_SESSION_STATE_DH
+         * */
+
+        if ((session->session_state != SSH_SESSION_STATE_AUTHENTICATED) &&
+            (session->session_state != SSH_SESSION_STATE_INITIAL_KEX))
+        {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if ((session->dh_handshake_state != DH_STATE_INIT) &&
+            (session->dh_handshake_state != DH_STATE_FINISHED))
+        {
+            rc = SSH_PACKET_DENIED;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_NEWKEYS:                            // 21
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_DH
+         * - dh_handshake_state == DH_STATE_NEWKEYS_SENT
+         *
+         * Transitions:
+         * - session->dh_handshake_state = DH_STATE_FINISHED
+         * - session->session_state = SSH_SESSION_STATE_AUTHENTICATING
+         * if session->flags & SSH_SESSION_FLAG_AUTHENTICATED
+         * - session->session_state = SSH_SESSION_STATE_AUTHENTICATED
+         * */
+
+        /* If DH has not been started, reject message */
+        if (session->session_state != SSH_SESSION_STATE_DH) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        /* Only allowed if dh_handshake_state is in NEWKEYS_SENT state */
+        if (session->dh_handshake_state != DH_STATE_NEWKEYS_SENT) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_KEXDH_INIT:                         // 30
+      // SSH2_MSG_KEX_ECDH_INIT:                      // 30
+      // SSH2_MSG_ECMQV_INIT:                         // 30
+      // SSH2_MSG_KEX_DH_GEX_REQUEST_OLD:             // 30
+
+        /* Server only */
+
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_DH
+         * - dh_handshake_state == DH_STATE_INIT
+         *
+         * Transitions:
+         * - session->dh_handshake_state = DH_STATE_INIT_SENT
+         * then calls dh_handshake_server which triggers:
+         * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_DH) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        /* Only allowed if dh_handshake_state is in initial state */
+        if (session->dh_handshake_state != DH_STATE_INIT) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_KEXDH_REPLY:                        // 31
+      // SSH2_MSG_KEX_ECDH_REPLY:                     // 31
+      // SSH2_MSG_ECMQV_REPLY:                        // 31
+      // SSH2_MSG_KEX_DH_GEX_GROUP:                   // 31
+
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_DH
+         * - dh_handshake_state == DH_STATE_INIT_SENT
+         *
+         * Transitions:
+         * - session->dh_handhsake_state = DH_STATE_NEWKEYS_SENT
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_DH) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_INIT_SENT) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_KEX_DH_GEX_INIT:                    // 32
+        /* TODO Not filtered */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_KEX_DH_GEX_REPLY:                   // 33
+        /* TODO Not filtered */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_KEX_DH_GEX_REQUEST:                 // 34
+        /* TODO Not filtered */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_REQUEST:                   // 50
+        /* Server only */
+
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         * - dh_hanshake_state == DH_STATE_FINISHED
+         *
+         * Transitions:
+         * - if authentication was successful:
+         *   - session_state = SSH_SESSION_STATE_AUTHENTICATED
+         * */
+
+        /* If this is a client, reject the message */
+        if (session->client) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_FINISHED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_FAILURE:                   // 51
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         * - dh_hanshake_state == DH_STATE_FINISHED
+         * - session->auth_state == SSH_AUTH_STATE_KBDINT_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
+         *
+         * Transitions:
+         * - if unpacking failed:
+         *   - session->auth_state = SSH_AUTH_ERROR
+         * - if failure was partial:
+         *   - session->auth_state = SSH_AUTH_PARTIAL
+         * - else:
+         *   - session->auth_state = SSH_AUTH_STATE_FAILED
+         * */
+
+        /* If this is a server, reject the message */
+        if (session->server) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_FINISHED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_SUCCESS:                   // 52
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         * - dh_hanshake_state == DH_STATE_FINISHED
+         * - session->auth_state == SSH_AUTH_STATE_KBDINT_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
+         *   or session->auth_state == SSH_AUTH_STATE_AUTH_NONE_SENT
+         *
+         * Transitions:
+         * - session->auth_state = SSH_AUTH_STATE_SUCCESS
+         * - session->session_state = SSH_SESSION_STATE_AUTHENTICATED
+         * - session->flags |= SSH_SESSION_FLAG_AUTHENTICATED
+         * - sessions->auth.current_method = SSH_AUTH_METHOD_UNKNOWN
+         * */
+
+        /* If this is a server, reject the message */
+        if (session->server) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_FINISHED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if ((session->auth_state != SSH_AUTH_STATE_KBDINT_SENT) &&
+            (session->auth_state != SSH_AUTH_STATE_PUBKEY_AUTH_SENT) &&
+            (session->auth_state != SSH_AUTH_STATE_PASSWORD_AUTH_SENT) &&
+            (session->auth_state != SSH_AUTH_STATE_GSSAPI_MIC_SENT) &&
+            (session->auth_state != SSH_AUTH_STATE_AUTH_NONE_SENT))
+        {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_BANNER:                    // 53
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_PK_OK:                     // 60
+      // SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ:          // 60
+      // SSH2_MSG_USERAUTH_INFO_REQUEST:              // 60
+      // SSH2_MSG_USERAUTH_GSSAPI_RESPONSE:           // 60
+
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         * - session->auth_state == SSH_AUTH_STATE_KBDINT_SENT
+         *   or
+         *   session->auth_state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT
+         *   or
+         *   session->auth_state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
+         *
+         * Transitions:
+         * Depending on the current state, the message is treated
+         * differently:
+         * - session->auth_state == SSH_AUTH_STATE_KBDINT_SENT
+         *   - session->auth_state = SSH_AUTH_STATE_INFO
+         * - session->auth_state == SSH_AUTH_STATE_GSSAPI_REQUEST_SENT
+         *   - session->auth_state = SSH_AUTH_STATE_GSSAPI_TOKEN
+         * - session->auth_state == SSH_AUTH_STATE_PUBKEY_OFFER_SENT
+         *   - session->auth_state = SSH_AUTH_STATE_PK_OK
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if ((session->auth_state != SSH_AUTH_STATE_KBDINT_SENT) &&
+            (session->auth_state != SSH_AUTH_STATE_PUBKEY_OFFER_SENT) &&
+            (session->auth_state != SSH_AUTH_STATE_GSSAPI_REQUEST_SENT))
+        {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_INFO_RESPONSE:             // 61
+      // SSH2_MSG_USERAUTH_GSSAPI_TOKEN:              // 61
+
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         * - session_state->auth_state == SSH_SESSION_STATE_GSSAPI_TOKEN
+         *   or
+         *   session_state->auth_state == SSH_SESSION_STATE_INFO
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if ((session->auth_state != SSH_AUTH_STATE_INFO) &&
+            (session->auth_state != SSH_AUTH_STATE_GSSAPI_TOKEN))
+        {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE:  // 63
+        /* TODO Not filtered */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_GSSAPI_ERROR:              // 64
+        /* TODO Not filtered */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_GSSAPI_ERRTOK:             // 65
+        /* TODO Not filtered */
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_USERAUTH_GSSAPI_MIC:                // 66
+        /* Server only */
+
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATING
+         * - session->gssapi->state == SSH_GSSAPI_STATE_RCV_MIC
+         *
+         * Transitions:
+         * Depending on the result of the verification, the states are
+         * changed:
+         * - SSH_AUTH_SUCCESS:
+         *   - session->session_state = SSH_SESSION_STATE_AUTHENTICATED
+         *   - session->flags != SSH_SESSION_FLAG_AUTHENTICATED
+         * - SSH_AUTH_PARTIAL:
+         *   - None
+         * - any other case:
+         *   - None
+         * */
+
+        /* If this is a client, reject the message */
+        if (session->client) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->dh_handshake_state != DH_STATE_FINISHED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_GLOBAL_REQUEST:                     // 80
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_REQUEST_SUCCESS:                    // 81
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
+         *
+         * Transitions:
+         * - session->global_req_state == SSH_CHANNEL_REQ_STATE_ACCEPTED
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_REQUEST_FAILURE:                    // 82
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         * - session->global_req_state == SSH_CHANNEL_REQ_STATE_PENDING
+         *
+         * Transitions:
+         * - session->global_req_state == SSH_CHANNEL_REQ_STATE_DENIED
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        if (session->global_req_state != SSH_CHANNEL_REQ_STATE_PENDING) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_OPEN:                       // 90
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION:          // 91
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - channel->state = SSH_CHANNEL_STATE_OPEN
+         * - channel->flags &= ~SSH_CHANNEL_FLAG_NOT_BOUND
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_OPEN_FAILURE:               // 92
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - channel->state = SSH_CHANNEL_STATE_OPEN_DENIED
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_WINDOW_ADJUST:              // 93
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_DATA:                       // 94
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_EXTENDED_DATA:              // 95
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_EOF:                        // 96
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - None
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_CLOSE:                      // 97
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - channel->state = SSH_CHANNEL_STATE_CLOSED
+         * - channel->flags |= SSH_CHANNEL_FLAG_CLOSED_REMOTE
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_REQUEST:                    // 98
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         *
+         * Transitions:
+         * - Depends on the request
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_SUCCESS:                    // 99
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+         *
+         * Transitions:
+         * - channel->request_state = SSH_CHANNEL_REQ_STATE_ACCEPTED
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    case SSH2_MSG_CHANNEL_FAILURE:                    // 100
+        /*
+         * States required:
+         * - session_state == SSH_SESSION_STATE_AUTHENTICATED
+         * - channel->request_state == SSH_CHANNEL_REQ_STATE_PENDING
+         *
+         * Transitions:
+         * - channel->request_state = SSH_CHANNEL_REQ_STATE_DENIED
+         * */
+
+        if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED) {
+            rc = SSH_PACKET_DENIED;
+            break;
+        }
+
+        rc = SSH_PACKET_ALLOWED;
+        break;
+    default:
+        /* Unknown message, do not filter */
+        rc = SSH_PACKET_UNKNOWN;
+        goto end;
+    }
+
+end:
+#ifdef DEBUG_PACKET
+    if (rc == SSH_PACKET_DENIED) {
+        SSH_LOG(SSH_LOG_PACKET, "REJECTED packet type %d: ",
+                session->in_packet.type);
+    }
+
+    if (rc == SSH_PACKET_UNKNOWN) {
+        SSH_LOG(SSH_LOG_PACKET, "UNKNOWN packet type %d",
+                session->in_packet.type);
+    }
+#endif
+
+    return rc;
+}
+
 /* in nonblocking mode, socket_read will read as much as it can, and return */
 /* SSH_OK if it has read at least len bytes, otherwise, SSH_AGAIN. */
 /* in blocking mode, it will read at least len bytes and will block until it's ok. */
@@ -153,6 +922,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
     uint32_t len, compsize, payloadsize;
     uint8_t padding;
     size_t processed = 0; /* number of byte processed from the callback */
+    enum ssh_packet_filter_result_e filter_result;
 
     if(session->current_crypto != NULL) {
       current_macsize = hmac_digest_len(session->current_crypto->in_hmac);
@@ -328,8 +1098,21 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
                     "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]",
                     session->in_packet.type, len, padding, compsize, payloadsize);
 
-            /* Execute callbacks */
-            ssh_packet_process(session, session->in_packet.type);
+            /* Check if the packet is expected */
+            filter_result = ssh_packet_incoming_filter(session);
+
+            switch(filter_result) {
+            case SSH_PACKET_ALLOWED:
+                /* Execute callbacks */
+                ssh_packet_process(session, session->in_packet.type);
+                break;
+            case SSH_PACKET_DENIED:
+                goto error;
+            case SSH_PACKET_UNKNOWN:
+                ssh_packet_send_unimplemented(session, session->recv_seq - 1);
+                break;
+            }
+
             session->packet_state = PACKET_STATE_INIT;
             if (processed < receivedlen) {
                 /* Handle a potential packet left in socket buffer */
-- 
2.19.1

