LCOV - code coverage report
Current view: top level - src/infrastructure - auth_session.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 87.6 % 129 113
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 5 5

            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           51 : 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           51 :     tl_writer_init(&w);
      26              : 
      27              :     /* auth.sendCode#a677244f phone_number:string api_id:int api_hash:string
      28              :      *                        settings:CodeSettings */
      29           51 :     tl_write_uint32(&w, CRC_auth_sendCode);
      30           51 :     tl_write_string(&w, phone);
      31           51 :     tl_write_int32(&w,  cfg->api_id);
      32           51 :     tl_write_string(&w, cfg->api_hash ? cfg->api_hash : "");
      33              : 
      34              :     /* codeSettings#ad253d78 flags:# (flags=0 → no special options) */
      35           51 :     tl_write_uint32(&w, CRC_codeSettings);
      36           51 :     tl_write_uint32(&w, 0); /* flags = 0 */
      37              : 
      38           51 :     if (w.len > max_len) {
      39            0 :         tl_writer_free(&w);
      40            0 :         return -1;
      41              :     }
      42           51 :     memcpy(out, w.data, w.len);
      43           51 :     *out_len = w.len;
      44           51 :     tl_writer_free(&w);
      45           51 :     return 0;
      46              : }
      47              : 
      48              : /* ---- Internal: skip a sentCodeType sub-object ---- */
      49              : 
      50           20 : static int skip_sent_code_type(TlReader *r) {
      51           20 :     if (!tl_reader_ok(r)) return -1;
      52           20 :     uint32_t type_crc = tl_read_uint32(r);
      53              : 
      54           20 :     switch (type_crc) {
      55           14 :     case CRC_auth_sentCodeTypeApp:
      56              :     case CRC_auth_sentCodeTypeSms:
      57              :     case CRC_auth_sentCodeTypeCall:
      58              :         /* length:int */
      59           14 :         tl_read_int32(r);
      60           14 :         return 0;
      61              : 
      62            3 :     case CRC_auth_sentCodeTypeFlashCall: {
      63              :         /* pattern:string */
      64            3 :         RAII_STRING char *pattern = tl_read_string(r);
      65              :         (void)pattern;
      66            3 :         return 0;
      67              :     }
      68              : 
      69            3 :     default:
      70            3 :         logger_log(LOG_WARN, "auth_send_code: unknown sentCodeType 0x%08x", type_crc);
      71            3 :         return -1;
      72              :     }
      73              : }
      74              : 
      75              : /* ---- auth_send_code ---- */
      76              : 
      77           52 : int auth_send_code(const ApiConfig *cfg,
      78              :                    MtProtoSession *s, Transport *t,
      79              :                    const char *phone,
      80              :                    AuthSentCode *out,
      81              :                    RpcError *err) {
      82           52 :     if (!cfg || !s || !t || !phone || !out) return -1;
      83           51 :     if (err) { err->error_code = 0; err->error_msg[0] = '\0';
      84           44 :                err->migrate_dc = -1; err->flood_wait_secs = 0; }
      85              : 
      86              :     uint8_t query[4096];
      87           51 :     size_t qlen = 0;
      88           51 :     if (build_send_code(cfg, phone, query, sizeof(query), &qlen) != 0) {
      89            0 :         logger_log(LOG_ERROR, "auth_send_code: failed to build request");
      90            0 :         return -1;
      91              :     }
      92              : 
      93           51 :     RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
      94           51 :     if (!resp) return -1;
      95           51 :     size_t resp_len = 0;
      96              : 
      97           51 :     if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
      98            1 :         logger_log(LOG_ERROR, "auth_send_code: api_call failed");
      99            1 :         return -1;
     100              :     }
     101              : 
     102           50 :     if (resp_len < 4) {
     103            0 :         logger_log(LOG_ERROR, "auth_send_code: response too short");
     104            0 :         return -1;
     105              :     }
     106              : 
     107              :     /* Check for rpc_error */
     108              :     uint32_t constructor;
     109           50 :     memcpy(&constructor, resp, 4);
     110           50 :     if (constructor == TL_rpc_error) {
     111              :         RpcError perr;
     112           27 :         rpc_parse_error(resp, resp_len, &perr);
     113           27 :         logger_log(LOG_ERROR, "auth_send_code: RPC error %d: %s",
     114              :                    perr.error_code, perr.error_msg);
     115           27 :         if (err) *err = perr;
     116           27 :         return -1;
     117              :     }
     118              : 
     119           23 :     if (constructor != CRC_auth_sentCode) {
     120            3 :         logger_log(LOG_ERROR, "auth_send_code: unexpected constructor 0x%08x",
     121              :                    constructor);
     122            3 :         return -1;
     123              :     }
     124              : 
     125              :     /* Parse auth.sentCode:
     126              :      * flags:# type:auth.SentCodeType phone_code_hash:string
     127              :      * next_type:flags.1?auth.CodeType timeout:flags.2?int */
     128           20 :     TlReader r = tl_reader_init(resp, resp_len);
     129           20 :     tl_read_uint32(&r); /* skip constructor */
     130              : 
     131           20 :     uint32_t flags = tl_read_uint32(&r);
     132              : 
     133           20 :     if (skip_sent_code_type(&r) != 0) {
     134            3 :         logger_log(LOG_ERROR, "auth_send_code: failed to parse sentCodeType");
     135            3 :         return -1;
     136              :     }
     137              : 
     138           34 :     RAII_STRING char *hash = tl_read_string(&r);
     139           17 :     if (!hash) {
     140            0 :         logger_log(LOG_ERROR, "auth_send_code: failed to read phone_code_hash");
     141            0 :         return -1;
     142              :     }
     143              : 
     144           17 :     size_t hash_len = strlen(hash);
     145           17 :     if (hash_len >= AUTH_CODE_HASH_MAX) {
     146            0 :         logger_log(LOG_ERROR, "auth_send_code: phone_code_hash too long");
     147            0 :         return -1;
     148              :     }
     149           17 :     memcpy(out->phone_code_hash, hash, hash_len + 1);
     150              : 
     151              :     /* timeout: flags.2 */
     152           17 :     out->timeout = 0;
     153           17 :     if (flags & (1u << 2)) {
     154            3 :         out->timeout = tl_read_int32(&r);
     155              :     }
     156              : 
     157           17 :     logger_log(LOG_INFO, "auth_send_code: code sent (hash_len=%zu), timeout=%d",
     158           17 :                strlen(out->phone_code_hash), out->timeout);
     159           17 :     return 0;
     160              : }
     161              : 
     162              : /* ---- Internal: build auth.signIn request ---- */
     163              : 
     164           33 : static int build_sign_in(const char *phone, const char *hash, const char *code,
     165              :                           uint8_t *out, size_t max_len, size_t *out_len) {
     166              :     TlWriter w;
     167           33 :     tl_writer_init(&w);
     168              : 
     169              :     /* auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string
     170              :      *                      phone_code:flags.0?string */
     171           33 :     tl_write_uint32(&w, CRC_auth_signIn);
     172           33 :     tl_write_uint32(&w, 1u);  /* flags = 0x1 → phone_code present */
     173           33 :     tl_write_string(&w, phone);
     174           33 :     tl_write_string(&w, hash);
     175           33 :     tl_write_string(&w, code); /* flags.0 is set */
     176              : 
     177           33 :     if (w.len > max_len) {
     178            0 :         tl_writer_free(&w);
     179            0 :         return -1;
     180              :     }
     181           33 :     memcpy(out, w.data, w.len);
     182           33 :     *out_len = w.len;
     183           33 :     tl_writer_free(&w);
     184           33 :     return 0;
     185              : }
     186              : 
     187              : /* ---- auth_sign_in ---- */
     188              : 
     189           34 : int auth_sign_in(const ApiConfig *cfg,
     190              :                  MtProtoSession *s, Transport *t,
     191              :                  const char *phone,
     192              :                  const char *phone_code_hash,
     193              :                  const char *code,
     194              :                  int64_t *user_id_out,
     195              :                  RpcError *err) {
     196           34 :     if (!cfg || !s || !t || !phone || !phone_code_hash || !code) return -1;
     197           33 :     if (err) { err->error_code = 0; err->error_msg[0] = '\0';
     198           28 :                err->migrate_dc = -1; err->flood_wait_secs = 0; }
     199              : 
     200              :     uint8_t query[4096];
     201           33 :     size_t qlen = 0;
     202           33 :     if (build_sign_in(phone, phone_code_hash, code,
     203              :                       query, sizeof(query), &qlen) != 0) {
     204            0 :         logger_log(LOG_ERROR, "auth_sign_in: failed to build request");
     205            0 :         return -1;
     206              :     }
     207              : 
     208           33 :     RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
     209           33 :     if (!resp) return -1;
     210           33 :     size_t resp_len = 0;
     211              : 
     212           33 :     if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
     213            1 :         logger_log(LOG_ERROR, "auth_sign_in: api_call failed");
     214            1 :         return -1;
     215              :     }
     216              : 
     217           32 :     if (resp_len < 4) {
     218            0 :         logger_log(LOG_ERROR, "auth_sign_in: response too short");
     219            0 :         return -1;
     220              :     }
     221              : 
     222              :     uint32_t constructor;
     223           32 :     memcpy(&constructor, resp, 4);
     224              : 
     225           32 :     if (constructor == TL_rpc_error) {
     226              :         RpcError perr;
     227           21 :         rpc_parse_error(resp, resp_len, &perr);
     228           21 :         logger_log(LOG_ERROR, "auth_sign_in: RPC error %d: %s",
     229              :                    perr.error_code, perr.error_msg);
     230           21 :         if (err) *err = perr;
     231           21 :         return -1;
     232              :     }
     233              : 
     234           11 :     if (constructor != CRC_auth_authorization) {
     235            3 :         logger_log(LOG_ERROR, "auth_sign_in: unexpected constructor 0x%08x",
     236              :                    constructor);
     237            3 :         return -1;
     238              :     }
     239              : 
     240              :     /* Parse auth.authorization:
     241              :      * flags:# setup_password_required:flags.0?true
     242              :      *         otherwise_relogin_days:flags.1?int
     243              :      *         tmp_sessions:flags.0?int
     244              :      *         user:User */
     245            8 :     TlReader r = tl_reader_init(resp, resp_len);
     246            8 :     tl_read_uint32(&r); /* skip constructor */
     247            8 :     uint32_t flags = tl_read_uint32(&r);
     248              : 
     249              :     /* skip optional fields */
     250            8 :     if (flags & (1u << 1)) tl_read_int32(&r); /* otherwise_relogin_days */
     251              : 
     252              :     /* user starts with constructor + id */
     253            8 :     uint32_t user_crc = tl_read_uint32(&r);
     254            8 :     if (user_crc != TL_user && user_crc != TL_userFull) {
     255            3 :         logger_log(LOG_WARN, "auth_sign_in: unexpected user constructor 0x%08x",
     256              :                    user_crc);
     257              :         /* still consider successful — we authenticated */
     258            3 :         if (user_id_out) *user_id_out = 0;
     259            3 :         return 0;
     260              :     }
     261              : 
     262              :     /* user#3ff6ecb0: flags:# self:... id:long ... */
     263            5 :     tl_read_uint32(&r); /* flags */
     264            5 :     int64_t uid = tl_read_int64(&r);
     265            5 :     if (user_id_out) *user_id_out = uid;
     266              : 
     267            5 :     logger_log(LOG_INFO, "auth_sign_in: authenticated as user_id=%lld",
     268              :                (long long)uid);
     269            5 :     return 0;
     270              : }
        

Generated by: LCOV version 2.0-1