LCOV - code coverage report
Current view: top level - tests/functional - test_wrong_session_id.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 49 49
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /**
       2              :  * @file test_wrong_session_id.c
       3              :  * @brief TEST-29 — functional test: mock server injects wrong session_id;
       4              :  *        client rejects the frame.
       5              :  *
       6              :  * The unit tests in tests/unit/test_rpc.c (test_recv_encrypted_wrong_session_id)
       7              :  * exercise rpc_recv_encrypted() directly with fake frames and mock crypto.
       8              :  * This functional test goes one layer higher: it seeds a real MTProto session,
       9              :  * arms the mock server to emit a reply with a deliberately wrong session_id,
      10              :  * issues a real api_call(), and asserts that the call returns -1.
      11              :  *
      12              :  * Flow:
      13              :  *   1. mt_server_seed_session() → real auth key on disk and in mock server.
      14              :  *   2. mt_server_expect() → register a responder that queues the (tampered)
      15              :  *      reply; the client rejects the frame during rpc_recv_encrypted().
      16              :  *   3. mt_server_set_wrong_session_id_once() → next reply will have
      17              :  *      session_id XOR 0xFFFFFFFFFFFFFFFF in the plaintext.
      18              :  *   4. api_call() sends users.getUsers, receives the tampered frame, and
      19              :  *      must return -1.
      20              :  */
      21              : 
      22              : #include "test_helpers.h"
      23              : 
      24              : #include "mock_socket.h"
      25              : #include "mock_tel_server.h"
      26              : 
      27              : #include "api_call.h"
      28              : #include "mtproto_session.h"
      29              : #include "transport.h"
      30              : #include "app/session_store.h"
      31              : #include "tl_serial.h"
      32              : #include "tl_registry.h"
      33              : 
      34              : #include <stdio.h>
      35              : #include <stdlib.h>
      36              : #include <string.h>
      37              : #include <unistd.h>
      38              : 
      39              : /* ---- helpers ---- */
      40              : 
      41            2 : static void with_tmp_home_wsi(const char *tag) {
      42              :     char tmp[256];
      43            2 :     snprintf(tmp, sizeof(tmp), "/tmp/tg-cli-ft-wsi-%s", tag);
      44              :     char bin[512];
      45            2 :     snprintf(bin, sizeof(bin), "%s/.config/tg-cli/session.bin", tmp);
      46            2 :     (void)unlink(bin);
      47            2 :     setenv("HOME", tmp, 1);
      48            2 : }
      49              : 
      50            2 : static void connect_mock_wsi(Transport *t) {
      51            2 :     transport_init(t);
      52            2 :     ASSERT(transport_connect(t, "127.0.0.1", 443) == 0,
      53              :            "transport connects for wrong-session-id test");
      54              : }
      55              : 
      56            2 : static void init_cfg_wsi(ApiConfig *cfg) {
      57            2 :     api_config_init(cfg);
      58            2 :     cfg->api_id   = 12345;
      59            2 :     cfg->api_hash = "deadbeefcafebabef00dbaadfeedc0de";
      60            2 : }
      61              : 
      62              : /* ---- responder ---- */
      63              : 
      64              : /**
      65              :  * Emit a minimal reply.  The mock server calls this when the client's
      66              :  * request arrives, before the (tampered) reply is enqueued.  So this
      67              :  * function WILL run — we rely on it to actually queue the bad frame.
      68              :  */
      69            2 : static void on_get_users_bad_reply(MtRpcContext *ctx) {
      70              :     TlWriter w;
      71            2 :     tl_writer_init(&w);
      72            2 :     tl_write_uint32(&w, TL_vector);
      73            2 :     tl_write_uint32(&w, 0);
      74            2 :     mt_server_reply_result(ctx, w.data, w.len);
      75            2 :     tl_writer_free(&w);
      76            2 : }
      77              : 
      78              : /* ================================================================ */
      79              : /* Test case                                                        */
      80              : /* ================================================================ */
      81              : 
      82              : /**
      83              :  * @brief TEST-29 — wrong session_id in server reply must be rejected.
      84              :  *
      85              :  * The mock server is armed to flip all bits of the session_id in its next
      86              :  * reply.  rpc_recv_encrypted() verifies the decrypted session_id against
      87              :  * s->session_id and must return -1, causing api_call() to propagate -1 to
      88              :  * the caller.
      89              :  *
      90              :  * Note on sequencing: the mock server dispatches the client request
      91              :  * synchronously inside api_call() before the client attempts to read.
      92              :  * The responder fires (server-side) and enqueues the tampered reply.
      93              :  * The client then calls rpc_recv_encrypted(), detects the wrong session_id,
      94              :  * and returns -1.  mt_server_rpc_call_count() confirms the server did
      95              :  * receive and process the request.
      96              :  */
      97            2 : static void test_wrong_session_id_rejected(void) {
      98            2 :     with_tmp_home_wsi("reject");
      99              : 
     100            2 :     mt_server_init();
     101            2 :     mt_server_reset();
     102            2 :     ASSERT(mt_server_seed_session(2, NULL, NULL, NULL) == 0, "seed session");
     103              : 
     104              :     /* users.getUsers CRC — a simple query the mock server can handle. */
     105              : #define CRC_users_getUsers 0x0d91a548U
     106            2 :     mt_server_expect(CRC_users_getUsers, on_get_users_bad_reply, NULL);
     107              : 
     108              :     /* Arm the one-shot: next reply frame will have a wrong session_id. */
     109            2 :     mt_server_set_wrong_session_id_once();
     110              : 
     111            2 :     ApiConfig cfg;  init_cfg_wsi(&cfg);
     112            2 :     MtProtoSession s;  mtproto_session_init(&s);
     113            2 :     int dc = 0;
     114            2 :     ASSERT(session_store_load(&s, &dc) == 0, "session loaded");
     115              : 
     116            2 :     Transport t;  connect_mock_wsi(&t);
     117              : 
     118              :     /* Build users.getUsers(inputUserSelf) query. */
     119              : #define CRC_inputUserSelf 0xf7c1b13fU
     120              :     TlWriter q;
     121            2 :     tl_writer_init(&q);
     122            2 :     tl_write_uint32(&q, CRC_users_getUsers);
     123            2 :     tl_write_uint32(&q, TL_vector);
     124            2 :     tl_write_uint32(&q, 1);
     125            2 :     tl_write_uint32(&q, CRC_inputUserSelf);
     126            2 :     tl_write_uint64(&q, 0ULL);   /* access_hash */
     127              : 
     128              :     uint8_t resp[4096];
     129            2 :     size_t  resp_len = 0;
     130            2 :     int rc = api_call(&cfg, &s, &t, q.data, q.len, resp, sizeof(resp), &resp_len);
     131            2 :     tl_writer_free(&q);
     132              : 
     133            2 :     ASSERT(rc == -1, "api_call must return -1 when session_id is wrong");
     134              : 
     135              :     /* The mock server dispatched one RPC (used to queue the tampered reply). */
     136            2 :     ASSERT(mt_server_rpc_call_count() == 1,
     137              :            "mock server must have received exactly one RPC call");
     138              : 
     139            2 :     transport_close(&t);
     140            2 :     mt_server_reset();
     141              : }
     142              : 
     143              : /* ================================================================ */
     144              : /* Suite entry point                                                */
     145              : /* ================================================================ */
     146              : 
     147            2 : void run_wrong_session_id_tests(void) {
     148            2 :     RUN_TEST(test_wrong_session_id_rejected);
     149            2 : }
        

Generated by: LCOV version 2.0-1