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: skip a sentCodeType sub-object ---- */
49 :
50 8 : static int skip_sent_code_type(TlReader *r) {
51 8 : if (!tl_reader_ok(r)) return -1;
52 8 : uint32_t type_crc = tl_read_uint32(r);
53 :
54 8 : switch (type_crc) {
55 6 : case CRC_auth_sentCodeTypeApp:
56 : case CRC_auth_sentCodeTypeSms:
57 : case CRC_auth_sentCodeTypeCall:
58 : /* length:int */
59 6 : tl_read_int32(r);
60 6 : return 0;
61 :
62 1 : case CRC_auth_sentCodeTypeFlashCall: {
63 : /* pattern:string */
64 1 : RAII_STRING char *pattern = tl_read_string(r);
65 : (void)pattern;
66 1 : return 0;
67 : }
68 :
69 1 : default:
70 1 : logger_log(LOG_WARN, "auth_send_code: unknown sentCodeType 0x%08x", type_crc);
71 1 : return -1;
72 : }
73 : }
74 :
75 : /* ---- auth_send_code ---- */
76 :
77 22 : int auth_send_code(const ApiConfig *cfg,
78 : MtProtoSession *s, Transport *t,
79 : const char *phone,
80 : AuthSentCode *out,
81 : RpcError *err) {
82 22 : if (!cfg || !s || !t || !phone || !out) return -1;
83 22 : if (err) { err->error_code = 0; err->error_msg[0] = '\0';
84 22 : err->migrate_dc = -1; err->flood_wait_secs = 0; }
85 :
86 : uint8_t query[4096];
87 22 : size_t qlen = 0;
88 22 : 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 22 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
94 22 : if (!resp) return -1;
95 22 : size_t resp_len = 0;
96 :
97 22 : if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
98 0 : logger_log(LOG_ERROR, "auth_send_code: api_call failed");
99 0 : return -1;
100 : }
101 :
102 22 : 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 22 : memcpy(&constructor, resp, 4);
110 22 : if (constructor == TL_rpc_error) {
111 : RpcError perr;
112 13 : rpc_parse_error(resp, resp_len, &perr);
113 13 : logger_log(LOG_ERROR, "auth_send_code: RPC error %d: %s",
114 : perr.error_code, perr.error_msg);
115 13 : if (err) *err = perr;
116 13 : return -1;
117 : }
118 :
119 9 : if (constructor != CRC_auth_sentCode) {
120 1 : logger_log(LOG_ERROR, "auth_send_code: unexpected constructor 0x%08x",
121 : constructor);
122 1 : 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 8 : TlReader r = tl_reader_init(resp, resp_len);
129 8 : tl_read_uint32(&r); /* skip constructor */
130 :
131 8 : uint32_t flags = tl_read_uint32(&r);
132 :
133 8 : if (skip_sent_code_type(&r) != 0) {
134 1 : logger_log(LOG_ERROR, "auth_send_code: failed to parse sentCodeType");
135 1 : return -1;
136 : }
137 :
138 14 : RAII_STRING char *hash = tl_read_string(&r);
139 7 : if (!hash) {
140 0 : logger_log(LOG_ERROR, "auth_send_code: failed to read phone_code_hash");
141 0 : return -1;
142 : }
143 :
144 7 : size_t hash_len = strlen(hash);
145 7 : 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 7 : memcpy(out->phone_code_hash, hash, hash_len + 1);
150 :
151 : /* timeout: flags.2 */
152 7 : out->timeout = 0;
153 7 : if (flags & (1u << 2)) {
154 1 : out->timeout = tl_read_int32(&r);
155 : }
156 :
157 7 : logger_log(LOG_INFO, "auth_send_code: code sent (hash_len=%zu), timeout=%d",
158 7 : strlen(out->phone_code_hash), out->timeout);
159 7 : return 0;
160 : }
161 :
162 : /* ---- Internal: build auth.signIn request ---- */
163 :
164 14 : 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 14 : tl_writer_init(&w);
168 :
169 : /* auth.signIn#8d52a951 flags:# phone_number:string phone_code_hash:string
170 : * phone_code:flags.0?string */
171 14 : tl_write_uint32(&w, CRC_auth_signIn);
172 14 : tl_write_uint32(&w, 1u); /* flags = 0x1 → phone_code present */
173 14 : tl_write_string(&w, phone);
174 14 : tl_write_string(&w, hash);
175 14 : tl_write_string(&w, code); /* flags.0 is set */
176 :
177 14 : if (w.len > max_len) {
178 0 : tl_writer_free(&w);
179 0 : return -1;
180 : }
181 14 : memcpy(out, w.data, w.len);
182 14 : *out_len = w.len;
183 14 : tl_writer_free(&w);
184 14 : return 0;
185 : }
186 :
187 : /* ---- auth_sign_in ---- */
188 :
189 14 : 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 14 : if (!cfg || !s || !t || !phone || !phone_code_hash || !code) return -1;
197 14 : if (err) { err->error_code = 0; err->error_msg[0] = '\0';
198 14 : err->migrate_dc = -1; err->flood_wait_secs = 0; }
199 :
200 : uint8_t query[4096];
201 14 : size_t qlen = 0;
202 14 : 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 14 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(65536);
209 14 : if (!resp) return -1;
210 14 : size_t resp_len = 0;
211 :
212 14 : if (api_call(cfg, s, t, query, qlen, resp, 65536, &resp_len) != 0) {
213 0 : logger_log(LOG_ERROR, "auth_sign_in: api_call failed");
214 0 : return -1;
215 : }
216 :
217 14 : 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 14 : memcpy(&constructor, resp, 4);
224 :
225 14 : if (constructor == TL_rpc_error) {
226 : RpcError perr;
227 10 : rpc_parse_error(resp, resp_len, &perr);
228 10 : logger_log(LOG_ERROR, "auth_sign_in: RPC error %d: %s",
229 : perr.error_code, perr.error_msg);
230 10 : if (err) *err = perr;
231 10 : return -1;
232 : }
233 :
234 4 : if (constructor != CRC_auth_authorization) {
235 1 : logger_log(LOG_ERROR, "auth_sign_in: unexpected constructor 0x%08x",
236 : constructor);
237 1 : 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 3 : TlReader r = tl_reader_init(resp, resp_len);
246 3 : tl_read_uint32(&r); /* skip constructor */
247 3 : uint32_t flags = tl_read_uint32(&r);
248 :
249 : /* skip optional fields */
250 3 : if (flags & (1u << 1)) tl_read_int32(&r); /* otherwise_relogin_days */
251 :
252 : /* user starts with constructor + id */
253 3 : uint32_t user_crc = tl_read_uint32(&r);
254 3 : if (user_crc != TL_user && user_crc != TL_userFull) {
255 1 : logger_log(LOG_WARN, "auth_sign_in: unexpected user constructor 0x%08x",
256 : user_crc);
257 : /* still consider successful — we authenticated */
258 1 : if (user_id_out) *user_id_out = 0;
259 1 : return 0;
260 : }
261 :
262 : /* user#3ff6ecb0: flags:# self:... id:long ... */
263 2 : tl_read_uint32(&r); /* flags */
264 2 : int64_t uid = tl_read_int64(&r);
265 2 : if (user_id_out) *user_id_out = uid;
266 :
267 2 : logger_log(LOG_INFO, "auth_sign_in: authenticated as user_id=%lld",
268 : (long long)uid);
269 2 : return 0;
270 : }
|