LCOV - code coverage report
Current view: top level - src/infrastructure - auth_session.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 53.8 % 225 121
Test Date: 2026-05-06 13:17:08 Functions: 66.7 % 9 6

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file auth_session.c
       6              :  * @brief Telegram phone-number login: auth.sendCode + auth.signIn.
       7              :  */
       8              : 
       9              : #include "auth_session.h"
      10              : #include "mtproto_rpc.h"
      11              : #include "tl_serial.h"
      12              : #include "tl_registry.h"
      13              : #include "logger.h"
      14              : #include "pii_redact.h"
      15              : #include "raii.h"
      16              : 
      17              : #include <stdlib.h>
      18              : #include <string.h>
      19              : 
      20              : /* ---- Internal: build auth.sendCode request ---- */
      21              : 
      22           22 : static int build_send_code(const ApiConfig *cfg, const char *phone,
      23              :                             uint8_t *out, size_t max_len, size_t *out_len) {
      24              :     TlWriter w;
      25           22 :     tl_writer_init(&w);
      26              : 
      27              :     /* auth.sendCode#a677244f phone_number:string api_id:int api_hash:string
      28              :      *                        settings:CodeSettings */
      29           22 :     tl_write_uint32(&w, CRC_auth_sendCode);
      30           22 :     tl_write_string(&w, phone);
      31           22 :     tl_write_int32(&w,  cfg->api_id);
      32           22 :     tl_write_string(&w, cfg->api_hash ? cfg->api_hash : "");
      33              : 
      34              :     /* codeSettings#ad253d78 flags:# (flags=0 → no special options) */
      35           22 :     tl_write_uint32(&w, CRC_codeSettings);
      36           22 :     tl_write_uint32(&w, 0); /* flags = 0 */
      37              : 
      38           22 :     if (w.len > max_len) {
      39            0 :         tl_writer_free(&w);
      40            0 :         return -1;
      41              :     }
      42           22 :     memcpy(out, w.data, w.len);
      43           22 :     *out_len = w.len;
      44           22 :     tl_writer_free(&w);
      45           22 :     return 0;
      46              : }
      47              : 
      48              : /* ---- Internal: read a sentCodeType sub-object, return its CRC ---- */
      49              : 
      50            8 : static uint32_t read_sent_code_type(TlReader *r) {
      51            8 :     if (!tl_reader_ok(r)) return 0;
      52            8 :     uint32_t type_crc = tl_read_uint32(r);
      53              : 
      54            8 :     logger_log(LOG_INFO, "auth_send_code: sentCodeType=0x%08x", type_crc);
      55              : 
      56            8 :     switch (type_crc) {
      57            6 :     case CRC_auth_sentCodeTypeApp:
      58              :     case CRC_auth_sentCodeTypeSms:
      59              :     case CRC_auth_sentCodeTypeCall: {
      60            6 :         int32_t code_len = tl_read_int32(r);
      61            6 :         logger_log(LOG_INFO, "auth_send_code: sentCodeType code_length=%d", code_len);
      62            6 :         return type_crc;
      63              :     }
      64              : 
      65            1 :     case CRC_auth_sentCodeTypeFlashCall: {
      66            1 :         RAII_STRING char *pattern = tl_read_string(r);
      67              :         (void)pattern;
      68            1 :         return type_crc;
      69              :     }
      70              : 
      71            1 :     default:
      72            1 :         logger_log(LOG_WARN, "auth_send_code: unknown sentCodeType 0x%08x", type_crc);
      73            1 :         return 0;
      74              :     }
      75              : }
      76              : 
      77              : /* ---- auth_send_code ---- */
      78              : 
      79           22 : int auth_send_code(const ApiConfig *cfg,
      80              :                    MtProtoSession *s, Transport *t,
      81              :                    const char *phone,
      82              :                    AuthSentCode *out,
      83              :                    RpcError *err) {
      84           22 :     if (!cfg || !s || !t || !phone || !out) return -1;
      85           22 :     if (err) { err->error_code = 0; err->error_msg[0] = '\0';
      86           22 :                err->migrate_dc = -1; err->flood_wait_secs = 0; }
      87              : 
      88              :     uint8_t query[4096];
      89           22 :     size_t qlen = 0;
      90           22 :     if (build_send_code(cfg, phone, query, sizeof(query), &qlen) != 0) {
      91            0 :         logger_log(LOG_ERROR, "auth_send_code: failed to build request");
      92            0 :         return -1;
      93              :     }
      94              : 
      95           22 :     RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
      96           22 :     if (!resp) return -1;
      97           22 :     size_t resp_len = 0;
      98              : 
      99           22 :     if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
     100            0 :         logger_log(LOG_ERROR, "auth_send_code: api_call failed");
     101            0 :         return -1;
     102              :     }
     103              : 
     104           22 :     if (resp_len < 4) {
     105            0 :         logger_log(LOG_ERROR, "auth_send_code: response too short");
     106            0 :         return -1;
     107              :     }
     108              : 
     109              :     /* Check for rpc_error */
     110              :     uint32_t constructor;
     111           22 :     memcpy(&constructor, resp, 4);
     112           22 :     if (constructor == TL_rpc_error) {
     113              :         RpcError perr;
     114           13 :         rpc_parse_error(resp, resp_len, &perr);
     115           13 :         logger_log(LOG_ERROR, "auth_send_code: RPC error %d: %s",
     116              :                    perr.error_code, perr.error_msg);
     117           13 :         if (err) *err = perr;
     118           13 :         return -1;
     119              :     }
     120              : 
     121            9 :     if (constructor != CRC_auth_sentCode) {
     122            1 :         logger_log(LOG_ERROR, "auth_send_code: unexpected constructor 0x%08x",
     123              :                    constructor);
     124            1 :         return -1;
     125              :     }
     126              : 
     127              :     /* Parse auth.sentCode:
     128              :      * flags:# type:auth.SentCodeType phone_code_hash:string
     129              :      * next_type:flags.1?auth.CodeType timeout:flags.2?int */
     130            8 :     TlReader r = tl_reader_init(resp, resp_len);
     131            8 :     tl_read_uint32(&r); /* skip constructor */
     132              : 
     133            8 :     uint32_t flags = tl_read_uint32(&r);
     134              : 
     135            8 :     out->code_type = read_sent_code_type(&r);
     136            8 :     if (out->code_type == 0) {
     137            1 :         logger_log(LOG_ERROR, "auth_send_code: failed to parse sentCodeType");
     138            1 :         return -1;
     139              :     }
     140              : 
     141           14 :     RAII_STRING char *hash = tl_read_string(&r);
     142            7 :     if (!hash) {
     143            0 :         logger_log(LOG_ERROR, "auth_send_code: failed to read phone_code_hash");
     144            0 :         return -1;
     145              :     }
     146              : 
     147            7 :     size_t hash_len = strlen(hash);
     148            7 :     if (hash_len >= AUTH_CODE_HASH_MAX) {
     149            0 :         logger_log(LOG_ERROR, "auth_send_code: phone_code_hash too long");
     150            0 :         return -1;
     151              :     }
     152            7 :     memcpy(out->phone_code_hash, hash, hash_len + 1);
     153              : 
     154              :     /* next_type: flags.1 */
     155            7 :     out->next_type = 0;
     156            7 :     if (flags & (1u << 1)) out->next_type = tl_read_uint32(&r);
     157              : 
     158              :     /* timeout: flags.2 */
     159            7 :     out->timeout = 0;
     160            7 :     if (flags & (1u << 2)) out->timeout = tl_read_int32(&r);
     161              : 
     162            7 :     logger_log(LOG_INFO,
     163              :                "auth_send_code: code sent (hash_len=%zu, code_type=0x%08x, "
     164              :                "next_type=0x%08x, timeout=%d)",
     165            7 :                strlen(out->phone_code_hash), out->code_type,
     166              :                out->next_type, out->timeout);
     167            7 :     return 0;
     168              : }
     169              : 
     170              : /* ---- Internal: build auth.signIn request ---- */
     171              : 
     172           14 : static int build_sign_in(const char *phone, const char *hash, const char *code,
     173              :                           uint8_t *out, size_t max_len, size_t *out_len) {
     174              :     TlWriter w;
     175           14 :     tl_writer_init(&w);
     176              : 
     177              :     /* auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string
     178              :      *                      phone_code:flags.0?string */
     179           14 :     tl_write_uint32(&w, CRC_auth_signIn);
     180           14 :     tl_write_uint32(&w, 1u);  /* flags = 0x1 → phone_code present */
     181           14 :     tl_write_string(&w, phone);
     182           14 :     tl_write_string(&w, hash);
     183           14 :     tl_write_string(&w, code); /* flags.0 is set */
     184              : 
     185           14 :     if (w.len > max_len) {
     186            0 :         tl_writer_free(&w);
     187            0 :         return -1;
     188              :     }
     189           14 :     memcpy(out, w.data, w.len);
     190           14 :     *out_len = w.len;
     191           14 :     tl_writer_free(&w);
     192           14 :     return 0;
     193              : }
     194              : 
     195              : /* ---- Internal: parse auth.authorization and extract user id ---- */
     196              : 
     197            3 : static int parse_authorization(const uint8_t *resp, size_t resp_len,
     198              :                                 int64_t *user_id_out, const char *caller) {
     199            3 :     TlReader r = tl_reader_init(resp, resp_len);
     200            3 :     tl_read_uint32(&r); /* skip constructor */
     201            3 :     uint32_t flags = tl_read_uint32(&r);
     202              : 
     203            3 :     if (flags & (1u << 1)) tl_read_int32(&r); /* otherwise_relogin_days */
     204              : 
     205            3 :     uint32_t user_crc = tl_read_uint32(&r);
     206            3 :     if (user_crc != TL_user && user_crc != TL_user2 && user_crc != TL_userFull) {
     207            1 :         logger_log(LOG_WARN, "%s: unexpected user constructor 0x%08x",
     208              :                    caller, user_crc);
     209            1 :         if (user_id_out) *user_id_out = 0;
     210            1 :         return 0;
     211              :     }
     212              : 
     213            2 :     tl_read_uint32(&r); /* user flags */
     214            2 :     int64_t uid = tl_read_int64(&r);
     215            2 :     if (user_id_out) *user_id_out = uid;
     216            2 :     logger_log(LOG_INFO, "%s: authenticated as user_id=%lld",
     217              :                caller, (long long)uid);
     218            2 :     return 0;
     219              : }
     220              : 
     221              : /* ---- auth_sign_in ---- */
     222              : 
     223           14 : int auth_sign_in(const ApiConfig *cfg,
     224              :                  MtProtoSession *s, Transport *t,
     225              :                  const char *phone,
     226              :                  const char *phone_code_hash,
     227              :                  const char *code,
     228              :                  int64_t *user_id_out,
     229              :                  int *signup_required,
     230              :                  RpcError *err) {
     231           14 :     if (!cfg || !s || !t || !phone || !phone_code_hash || !code) return -1;
     232           14 :     if (signup_required) *signup_required = 0;
     233           14 :     if (err) { err->error_code = 0; err->error_msg[0] = '\0';
     234           14 :                err->migrate_dc = -1; err->flood_wait_secs = 0; }
     235              : 
     236              :     uint8_t query[4096];
     237           14 :     size_t qlen = 0;
     238           14 :     if (build_sign_in(phone, phone_code_hash, code,
     239              :                       query, sizeof(query), &qlen) != 0) {
     240            0 :         logger_log(LOG_ERROR, "auth_sign_in: failed to build request");
     241            0 :         return -1;
     242              :     }
     243              : 
     244           14 :     logger_log(LOG_INFO, "auth_sign_in: sending request");
     245              : 
     246           14 :     RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
     247           14 :     if (!resp) return -1;
     248           14 :     size_t resp_len = 0;
     249              : 
     250           14 :     if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
     251            0 :         logger_log(LOG_ERROR, "auth_sign_in: api_call failed");
     252            0 :         return -1;
     253              :     }
     254              : 
     255           14 :     if (resp_len < 4) {
     256            0 :         logger_log(LOG_ERROR, "auth_sign_in: response too short");
     257            0 :         return -1;
     258              :     }
     259              : 
     260              :     uint32_t constructor;
     261           14 :     memcpy(&constructor, resp, 4);
     262              : 
     263           14 :     if (constructor == TL_rpc_error) {
     264              :         RpcError perr;
     265            9 :         rpc_parse_error(resp, resp_len, &perr);
     266            9 :         logger_log(LOG_ERROR, "auth_sign_in: RPC error %d: %s",
     267              :                    perr.error_code, perr.error_msg);
     268            9 :         if (err) *err = perr;
     269            9 :         return -1;
     270              :     }
     271              : 
     272            5 :     if (constructor == CRC_auth_authorizationSignUpRequired) {
     273            1 :         logger_log(LOG_INFO, "auth_sign_in: sign-up required for this phone");
     274            1 :         if (signup_required) *signup_required = 1;
     275            1 :         return -1;
     276              :     }
     277              : 
     278            4 :     if (constructor != CRC_auth_authorization) {
     279            1 :         logger_log(LOG_ERROR, "auth_sign_in: unexpected constructor 0x%08x",
     280              :                    constructor);
     281            1 :         return -1;
     282              :     }
     283              : 
     284            3 :     return parse_authorization(resp, resp_len, user_id_out, "auth_sign_in");
     285              : }
     286              : 
     287              : /* ---- Internal: build auth.signUp request ---- */
     288              : 
     289            0 : static int build_sign_up(const char *phone, const char *hash,
     290              :                           const char *first_name, const char *last_name,
     291              :                           uint8_t *out, size_t max_len, size_t *out_len) {
     292              :     TlWriter w;
     293            0 :     tl_writer_init(&w);
     294              : 
     295              :     /* auth.signUp#80eee427 phone_number:string phone_code_hash:string
     296              :      *                      first_name:string last_name:string */
     297            0 :     tl_write_uint32(&w, CRC_auth_signUp);
     298            0 :     tl_write_string(&w, phone);
     299            0 :     tl_write_string(&w, hash);
     300            0 :     tl_write_string(&w, first_name);
     301            0 :     tl_write_string(&w, last_name ? last_name : "");
     302              : 
     303            0 :     if (w.len > max_len) {
     304            0 :         tl_writer_free(&w);
     305            0 :         return -1;
     306              :     }
     307            0 :     memcpy(out, w.data, w.len);
     308            0 :     *out_len = w.len;
     309            0 :     tl_writer_free(&w);
     310            0 :     return 0;
     311              : }
     312              : 
     313              : /* ---- auth_sign_up ---- */
     314              : 
     315            0 : int auth_sign_up(const ApiConfig *cfg,
     316              :                  MtProtoSession *s, Transport *t,
     317              :                  const char *phone,
     318              :                  const char *phone_code_hash,
     319              :                  const char *first_name,
     320              :                  const char *last_name,
     321              :                  int64_t *user_id_out,
     322              :                  RpcError *err) {
     323            0 :     if (!cfg || !s || !t || !phone || !phone_code_hash || !first_name) return -1;
     324            0 :     if (err) { err->error_code = 0; err->error_msg[0] = '\0';
     325            0 :                err->migrate_dc = -1; err->flood_wait_secs = 0; }
     326              : 
     327              :     uint8_t query[4096];
     328            0 :     size_t qlen = 0;
     329            0 :     if (build_sign_up(phone, phone_code_hash, first_name, last_name,
     330              :                       query, sizeof(query), &qlen) != 0) {
     331            0 :         logger_log(LOG_ERROR, "auth_sign_up: failed to build request");
     332            0 :         return -1;
     333              :     }
     334              : 
     335            0 :     logger_log(LOG_INFO, "auth_sign_up: phone='%s' first_name='%s'",
     336              :                phone, first_name);
     337              : 
     338            0 :     RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
     339            0 :     if (!resp) return -1;
     340            0 :     size_t resp_len = 0;
     341              : 
     342            0 :     if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
     343            0 :         logger_log(LOG_ERROR, "auth_sign_up: api_call failed");
     344            0 :         return -1;
     345              :     }
     346              : 
     347            0 :     if (resp_len < 4) {
     348            0 :         logger_log(LOG_ERROR, "auth_sign_up: response too short");
     349            0 :         return -1;
     350              :     }
     351              : 
     352              :     uint32_t constructor;
     353            0 :     memcpy(&constructor, resp, 4);
     354              : 
     355            0 :     if (constructor == TL_rpc_error) {
     356              :         RpcError perr;
     357            0 :         rpc_parse_error(resp, resp_len, &perr);
     358            0 :         logger_log(LOG_ERROR, "auth_sign_up: RPC error %d: %s",
     359              :                    perr.error_code, perr.error_msg);
     360            0 :         if (err) *err = perr;
     361            0 :         return -1;
     362              :     }
     363              : 
     364            0 :     if (constructor != CRC_auth_authorization) {
     365            0 :         logger_log(LOG_ERROR, "auth_sign_up: unexpected constructor 0x%08x",
     366              :                    constructor);
     367            0 :         return -1;
     368              :     }
     369              : 
     370            0 :     return parse_authorization(resp, resp_len, user_id_out, "auth_sign_up");
     371              : }
     372              : 
     373              : /* ---- auth_resend_code ---- */
     374              : 
     375            0 : int auth_resend_code(const ApiConfig *cfg,
     376              :                      MtProtoSession *s, Transport *t,
     377              :                      const char *phone,
     378              :                      AuthSentCode *sent,
     379              :                      RpcError *err) {
     380            0 :     if (!cfg || !s || !t || !phone || !sent) return -1;
     381            0 :     if (err) { err->error_code = 0; err->error_msg[0] = '\0';
     382            0 :                err->migrate_dc = -1; err->flood_wait_secs = 0; }
     383              : 
     384              :     TlWriter w;
     385            0 :     tl_writer_init(&w);
     386              : 
     387              :     /* auth.resendCode#3ef1a9bf flags:# phone_number:string phone_code_hash:string
     388              :      *                          reason:flags.0?string */
     389            0 :     tl_write_uint32(&w, CRC_auth_resendCode);
     390            0 :     tl_write_uint32(&w, 0);  /* flags = 0 (no reason) */
     391            0 :     tl_write_string(&w, phone);
     392            0 :     tl_write_string(&w, sent->phone_code_hash);
     393              : 
     394              :     uint8_t query[1024];
     395            0 :     size_t qlen = 0;
     396            0 :     if (w.len > sizeof(query)) { tl_writer_free(&w); return -1; }
     397            0 :     memcpy(query, w.data, w.len);
     398            0 :     qlen = w.len;
     399            0 :     tl_writer_free(&w);
     400              : 
     401            0 :     RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
     402            0 :     if (!resp) return -1;
     403            0 :     size_t resp_len = 0;
     404              : 
     405            0 :     if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
     406            0 :         logger_log(LOG_ERROR, "auth_resend_code: api_call failed");
     407            0 :         return -1;
     408              :     }
     409              : 
     410            0 :     if (resp_len < 4) {
     411            0 :         logger_log(LOG_ERROR, "auth_resend_code: response too short");
     412            0 :         return -1;
     413              :     }
     414              : 
     415              :     uint32_t constructor;
     416            0 :     memcpy(&constructor, resp, 4);
     417              : 
     418            0 :     if (constructor == TL_rpc_error) {
     419              :         RpcError perr;
     420            0 :         rpc_parse_error(resp, resp_len, &perr);
     421            0 :         logger_log(LOG_ERROR, "auth_resend_code: RPC error %d: %s",
     422              :                    perr.error_code, perr.error_msg);
     423            0 :         if (err) *err = perr;
     424            0 :         return -1;
     425              :     }
     426              : 
     427            0 :     if (constructor != CRC_auth_sentCode) {
     428            0 :         logger_log(LOG_ERROR, "auth_resend_code: unexpected constructor 0x%08x",
     429              :                    constructor);
     430            0 :         return -1;
     431              :     }
     432              : 
     433              :     /* Parse the new sentCode to update code_type. */
     434            0 :     TlReader r = tl_reader_init(resp, resp_len);
     435            0 :     tl_read_uint32(&r); /* skip constructor */
     436            0 :     uint32_t flags = tl_read_uint32(&r);
     437              : 
     438            0 :     sent->code_type = read_sent_code_type(&r);
     439              : 
     440              :     /* skip phone_code_hash — must not change */
     441            0 :     RAII_STRING char *new_hash = tl_read_string(&r);
     442              :     (void)new_hash;
     443              : 
     444            0 :     sent->next_type = 0;
     445            0 :     if (flags & (1u << 1)) sent->next_type = tl_read_uint32(&r);
     446            0 :     if (flags & (1u << 2)) sent->timeout   = tl_read_int32(&r);
     447              : 
     448            0 :     logger_log(LOG_INFO,
     449              :                "auth_resend_code: new code_type=0x%08x, next_type=0x%08x",
     450              :                sent->code_type, sent->next_type);
     451            0 :     return 0;
     452              : }
        

Generated by: LCOV version 2.0-1