]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #5148: wizard: improve MMS presentation context search logic
authorViktor Chyzhovych -X (vchyzhov - SOFTSERVE INC at Cisco) <vchyzhov@cisco.com>
Thu, 12 Feb 2026 12:03:12 +0000 (12:03 +0000)
committerOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Thu, 12 Feb 2026 12:03:12 +0000 (12:03 +0000)
Merge in SNORT/snort3 from ~VCHYZHOV/snort3:mms_curse_pres_ctx_improve to master

Squashed commit of the following:

commit b02b09a48fe9dd6f0911df32406d889c82bac965
Author: Viktor Chyzhovych <vchyzhov@cisco.com>
Date:   Wed Feb 11 12:08:00 2026 +0200

    wizard: additional coverage of unit tests

commit 99be40298a0f2f559b529f3c6b826c0f93a64e5b
Author: Viktor Chyzhovych <vchyzhov@cisco.com>
Date:   Fri Feb 6 11:00:54 2026 +0200

    wizard: improve MMS presentation context search logic

src/service_inspectors/wizard/mms_curse.cc
src/service_inspectors/wizard/mms_curse.h

index 2b6d16d5f1777d81accaa126066cce189ce9d26d..4fe0e45b52354fedcf7961f5edcf79e381119c27 100644 (file)
@@ -107,73 +107,100 @@ static uint32_t search_for_osi_session_spdu( MmsTracker& mms, const uint8_t* dat
 
 static uint32_t search_for_pres_ctx( MmsTracker& mms, const uint8_t* data, unsigned len, uint32_t idx )
 {
-    // define some constants for search windows
-    constexpr uint32_t fully_encoded_data_window = 3;
-    constexpr uint32_t pres_ctx_window = 3;
-    constexpr uint32_t max_depth_idx = fully_encoded_data_window + pres_ctx_window;
-
-    // try to determine if the ACSE layer exists
-    // len reduction is to allow space for the forward looking checks
-    bool ctx_likely = false;
-    for ( uint32_t init_byte = idx; ( init_byte < len - max_depth_idx ) && !ctx_likely; init_byte++)
+    assert( data );
+
+    // Maximum length bytes to skip before resetting pattern search
+    constexpr uint32_t MAX_SKIP_BYTES = 2;
+
+    enum
     {
-        // require the user-data fully encoded data tag ( | 61 | )
-        if ( data[init_byte] != 0x61 )
-        {
-            continue;
-        }
+        USER_DATA_TAG = 0x61,
+        ENCODED_DATA_TAG = 0x30,
+        PRES_CTX_TAG = 0x02,
+        PRES_CTX_LEN = 0x01,
+    };
 
-        // make sure there is still enough space left in the buffer for the forward search
-        // allow up to two bytes for the fully encoded data field
-        if ( !verify_search_depth_idx( len, init_byte, max_depth_idx ) )
+    while ( idx < len )
+    {
+        switch ( mms.state )
         {
-            // not enough data to process
-            assert( !ctx_likely );
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG:
+            if ( data[idx] == USER_DATA_TAG )
+            {
+                mms.state = MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN;
+                mms.state_remain = 0;
+            }
             break;
-        }
 
-        for ( uint32_t encode_tag_shift = 1; ( encode_tag_shift < fully_encoded_data_window ) && !ctx_likely; encode_tag_shift++ )
-        {
-            uint32_t encode_tag_byte = init_byte + encode_tag_shift;
-            // look for the ' fully encoded data ' user data tag ( | 30 | );
-            if ( data[encode_tag_byte] != 0x30 )
+        case MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN:
+            if ( data[idx] == ENCODED_DATA_TAG )
+            {
+                mms.state = MMS_STATE__OSI_PRES_CTX_SKIP_ENCODED_DATA_LEN;
+                mms.state_remain = 0;
+            }
+            else if ( data[idx] == USER_DATA_TAG )
             {
-                continue;
+                mms.state = MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN;
+                mms.state_remain = 0;
             }
+            else if ( ++mms.state_remain > MAX_SKIP_BYTES )
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+            break;
 
-            for ( uint32_t pres_ctx_shift = 1; ( pres_ctx_shift < + pres_ctx_window ) && !ctx_likely; pres_ctx_shift++ )
+        case MMS_STATE__OSI_PRES_CTX_SKIP_ENCODED_DATA_LEN:
+            if ( data[idx] == PRES_CTX_TAG )
             {
-                // look for the presentation context tag and length ( | 02 01 | )
-                bool ctx_b1 = data[encode_tag_byte + pres_ctx_shift] == 0x02;
-                bool ctx_b2 = data[encode_tag_byte + pres_ctx_shift + 1] == 0x01;
-                if ( ctx_b1 && ctx_b2 )
-                {
-                    switch ( data[encode_tag_byte + pres_ctx_shift + 2] )
-                    {
-                    // set the state accordingly when the OSI ACSE presentation context ( | 01 | ) has been found
-                    case PresCtx::PRES_CTX_ACSE:
-                    {
-                        // place the index at the last byte of the presentation context
-                        idx = init_byte + encode_tag_shift + pres_ctx_shift + 2;
-                        mms.state = MMS_STATE__OSI_ACSE;
-                        ctx_likely = true;
-                        break;
-                    }
-                    // set the state accordingly when the MMS presentation context ( | 03 | ) has been found
-                    case PresCtx::PRES_CTX_MMS:
-                    {
-                        // place the index at the last byte of the presentation context
-                        idx = init_byte + encode_tag_shift + pres_ctx_shift + 2;
-                        mms.state = MMS_STATE__MMS;
-                        ctx_likely = true;
-                        break;
-                    }
-                    // no default as we want to keep looking if an acceptable context is not found
-                    }
-                }
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN;
+                mms.state_remain = 0;
+            }
+            else if ( data[idx] == USER_DATA_TAG )
+            {
+                mms.state = MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN;
+                mms.state_remain = 0;
             }
+            else if ( ++mms.state_remain > MAX_SKIP_BYTES )
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+            break;
+
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_TAG:
+            if ( data[idx] == PRES_CTX_TAG )
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN;
+            else
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+            break;
+
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN:
+            if ( data[idx] == PRES_CTX_LEN )
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT;
+            else
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+            break;
+
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT:
+            switch ( data[idx] )
+            {
+            case PresCtx::PRES_CTX_ACSE:
+                mms.state = MMS_STATE__OSI_ACSE;
+                return idx;
+
+            case PresCtx::PRES_CTX_MMS:
+                mms.state = MMS_STATE__MMS;
+                return idx;
+
+            default:
+                mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+                break;
+            }
+            break;
+
+        default:
+            assert( false );
+            break;
         }
+
+        idx++;
     }
+
     return idx;
 }
 
@@ -650,8 +677,9 @@ bool CurseBook::mms_curse( const uint8_t* data, unsigned len, CurseTracker* trac
         // process the User Data Presentation type
         case MMS_STATE__OSI_PRES_USER_DATA:
         {
-            idx = search_for_pres_ctx(mms, data, len, idx);
-            break;
+            mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+            // Avoid processing current byte twice
+            continue;
         }
 
 
@@ -703,8 +731,21 @@ bool CurseBook::mms_curse( const uint8_t* data, unsigned len, CurseTracker* trac
             break;
         }
 
-        //
+        // Initialize presentation context search
         case MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE:
+        {
+            mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+            continue;
+        }
+
+        // Presentation context search states
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG:      // fallthrough intentional
+        case MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN:        // fallthrough intentional
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_ENCODED_DATA_TAG:   // fallthrough intentional
+        case MMS_STATE__OSI_PRES_CTX_SKIP_ENCODED_DATA_LEN:     // fallthrough intentional
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_TAG:       // fallthrough intentional
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN:       // fallthrough intentional
+        case MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT:
         {
             idx = search_for_pres_ctx(mms, data, len, idx);
             break;
@@ -1337,4 +1378,480 @@ TEST_CASE("search_for_osi_acse_data: single byte buffer at each state", "[mms][a
     CHECK(result3 == 1);
 }
 
+TEST_CASE("search_for_pres_ctx: complete pattern found", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 4);
+}
+
+TEST_CASE("search_for_pres_ctx: ACSE context found", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02, 0x01, 0x01 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_ACSE));
+    CHECK(result == 4);
+}
+
+TEST_CASE("search_for_pres_ctx: small payload no pattern", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0xFF };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: small payload partial match", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: buffer without 0x61 byte", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: buffer boundary protection", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: pattern with offset", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0xFF, 0xAA, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 6);
+}
+
+TEST_CASE("search_for_pres_ctx: wrong encoded data tag resets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0xFF, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 6);
+}
+
+TEST_CASE("search_for_pres_ctx: wrong pres_ctx tag resets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0xFF, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 7);
+}
+
+TEST_CASE("search_for_pres_ctx: wrong length resets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02, 0xFF, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 8);
+}
+
+TEST_CASE("search_for_pres_ctx: unknown context value resets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02, 0x01, 0xFF, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 9);
+}
+
+TEST_CASE("search_for_pres_ctx: single byte at buffer end", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0xFF, 0xAA, 0x61 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: empty buffer", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[1] = { };
+    unsigned len = 0;
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == 0);
+}
+
+TEST_CASE("search_for_pres_ctx: resume from skip_user_data_len state", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 3);
+}
+
+TEST_CASE("search_for_pres_ctx: resume with wrong byte resets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0xFF, 0xFF, 0xFF, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 7);
+}
+
+TEST_CASE("search_for_pres_ctx: pattern at exact buffer end", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 4);
+}
+
+TEST_CASE("search_for_pres_ctx: multiple false starts", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0xFF, 0x61, 0x30, 0xFF, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 9);
+}
+
+TEST_CASE("search_for_pres_ctx: fragmented across packets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data1[] = { 0x61, 0x30 };
+    uint32_t result1 = search_for_pres_ctx(mms, data1, sizeof(data1), 0);
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SKIP_ENCODED_DATA_LEN));
+    CHECK(result1 == sizeof(data1));
+
+    uint8_t data2[] = { 0x02, 0x01 };
+    uint32_t result2 = search_for_pres_ctx(mms, data2, sizeof(data2), 0);
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT));
+    CHECK(result2 == sizeof(data2));
+
+    uint8_t data3[] = { 0x03 };
+    uint32_t result3 = search_for_pres_ctx(mms, data3, sizeof(data3), 0);
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result3 == 0);
+}
+
+TEST_CASE("search_for_pres_ctx: OOB protection with partial match", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0x02, 0x01 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: ASN.1 with length bytes", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x14, 0x30, 0x12, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 6);
+}
+
+TEST_CASE("search_for_pres_ctx: max skip bytes boundary exactly 2 bytes", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0xFF, 0xAA, 0xBB, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: skip encoded data len exceeds max skip bytes", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x30, 0xFF, 0xAA, 0xBB, 0x61, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 9);
+}
+
+TEST_CASE("search_for_pres_ctx: fragmented in length field", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data1[] = { 0x61, 0x14 };
+    uint32_t result1 = search_for_pres_ctx(mms, data1, sizeof(data1), 0);
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN));
+    CHECK(result1 == sizeof(data1));
+
+    uint8_t data2[] = { 0x30, 0x12, 0x02, 0x01 };
+    uint32_t result2 = search_for_pres_ctx(mms, data2, sizeof(data2), 0);
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT));
+    CHECK(result2 == sizeof(data2));
+
+    uint8_t data3[] = { 0x03 };
+    uint32_t result3 = search_for_pres_ctx(mms, data3, sizeof(data3), 0);
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result3 == 0);
+}
+
+TEST_CASE("search_for_pres_ctx: 2-byte ASN.1 length encoding", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x82, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 5);
+}
+
+TEST_CASE("search_for_pres_ctx: state_remain resets correctly", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0x14, 0x30, 0x12, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__MMS));
+    CHECK(result == 6);
+}
+
+TEST_CASE("search_for_pres_ctx: exactly at max skip bytes then tag found", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x61, 0xFF, 0xAA, 0xBB, 0x30, 0x02, 0x01, 0x03 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: OOB read protection small payload", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x62, 0x63 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: resume from pres_ctx state", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0x02 };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN));
+    CHECK(result == len);
+}
+
+TEST_CASE("search_for_pres_ctx: resume from pres_ctx wrong tag resets", "[mms][pres_ctx]")
+{
+    MmsTracker mms;
+    mms.state = MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_TAG;
+    mms.state_remain = 0;
+
+    uint8_t data[] = { 0xFF };
+    unsigned len = sizeof(data);
+    uint32_t idx = 0;
+
+    uint32_t result = search_for_pres_ctx(mms, data, len, idx);
+
+    CHECK((mms.state == MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG));
+    CHECK(result == len);
+}
+
 #endif
index deb797d9915499f1e64eece0308d81a61007a200..c27a74f8b0ce9021e57ffc2ce900e9e2bd64f73d 100644 (file)
@@ -47,6 +47,13 @@ enum MMS_State
     MMS_STATE__OSI_PRES_CP_CPA,
     MMS_STATE__OSI_PRES_CP_CPA_USER_DATA_ACSE_LOCATE,
     MMS_STATE__OSI_PRES_USER_DATA,
+    MMS_STATE__OSI_PRES_CTX_SEARCH_USER_DATA_TAG,
+    MMS_STATE__OSI_PRES_CTX_SKIP_USER_DATA_LEN,
+    MMS_STATE__OSI_PRES_CTX_SEARCH_ENCODED_DATA_TAG,
+    MMS_STATE__OSI_PRES_CTX_SKIP_ENCODED_DATA_LEN,
+    MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_TAG,
+    MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_LEN,
+    MMS_STATE__OSI_PRES_CTX_SEARCH_PRES_CTX_CONTEXT,
     MMS_STATE__OSI_ACSE,
     MMS_STATE__OSI_ACSE_TYPE,
     MMS_STATE__OSI_ACSE_DATA,