Line data Source code
1 : /* SPDX-License-Identifier: GPL-3.0-or-later */
2 : /* Copyright 2026 Peter Csaszar */
3 :
4 : /**
5 : * @file infrastructure/auth_logout.c
6 : * @brief Server-side logout: auth.logOut#3e72ba19 + local session wipe.
7 : */
8 :
9 : #include "auth_logout.h"
10 : #include "app/session_store.h"
11 : #include "mtproto_rpc.h"
12 : #include "tl_serial.h"
13 : #include "tl_registry.h"
14 : #include "logger.h"
15 : #include "raii.h"
16 :
17 : #include <stdlib.h>
18 : #include <string.h>
19 :
20 : /* ---- Logout observer (Observer pattern keeps domain/ out of here) ---- */
21 :
22 : static void (*s_on_logout_cb)(void) = NULL;
23 :
24 10 : void auth_logout_set_cache_flush_cb(void (*cb)(void)) {
25 10 : s_on_logout_cb = cb;
26 10 : }
27 :
28 : /* ---- Build the auth.logOut TL request ---- */
29 :
30 12 : static int build_logout_request(uint8_t *out, size_t max_len, size_t *out_len) {
31 : TlWriter w;
32 12 : tl_writer_init(&w);
33 :
34 : /* auth.logOut#3e72ba19 — no arguments */
35 12 : tl_write_uint32(&w, CRC_auth_logOut);
36 :
37 12 : if (w.len > max_len) {
38 0 : tl_writer_free(&w);
39 0 : return -1;
40 : }
41 12 : memcpy(out, w.data, w.len);
42 12 : *out_len = w.len;
43 12 : tl_writer_free(&w);
44 12 : return 0;
45 : }
46 :
47 : /* ---- auth_logout_rpc ---- */
48 :
49 13 : int auth_logout_rpc(const ApiConfig *cfg, MtProtoSession *s, Transport *t) {
50 13 : if (!cfg || !s || !t) return -1;
51 :
52 : uint8_t query[64];
53 12 : size_t qlen = 0;
54 12 : if (build_logout_request(query, sizeof(query), &qlen) != 0) {
55 0 : logger_log(LOG_ERROR, "auth_logout: failed to build request");
56 0 : return -1;
57 : }
58 :
59 12 : RAII_STRING uint8_t *resp = (uint8_t *)malloc(4096);
60 12 : if (!resp) return -1;
61 12 : size_t resp_len = 0;
62 :
63 12 : if (api_call(cfg, s, t, query, qlen, resp, 4096, &resp_len) != 0) {
64 1 : logger_log(LOG_WARN, "auth_logout: api_call failed");
65 1 : return -1;
66 : }
67 :
68 11 : if (resp_len < 4) {
69 0 : logger_log(LOG_WARN, "auth_logout: response too short (%zu bytes)", resp_len);
70 0 : return -1;
71 : }
72 :
73 : uint32_t constructor;
74 11 : memcpy(&constructor, resp, 4);
75 :
76 11 : if (constructor == TL_rpc_error) {
77 : RpcError err;
78 4 : rpc_parse_error(resp, resp_len, &err);
79 4 : logger_log(LOG_WARN, "auth_logout: RPC error %d: %s",
80 : err.error_code, err.error_msg);
81 : /* AUTH_KEY_UNREGISTERED / NOT_AUTHORIZED mean the session is already
82 : * dead on the server — still counts as a successful logout. */
83 4 : if (err.error_code == 401) {
84 1 : logger_log(LOG_INFO, "auth_logout: session already invalid on server");
85 1 : return 0;
86 : }
87 3 : return -1;
88 : }
89 :
90 7 : if (constructor != CRC_auth_loggedOut) {
91 0 : logger_log(LOG_WARN, "auth_logout: unexpected constructor 0x%08x", constructor);
92 0 : return -1;
93 : }
94 :
95 : /* auth.loggedOut#c3a2835f flags:# future_auth_token:flags.0?bytes
96 : * We intentionally ignore future_auth_token. */
97 7 : logger_log(LOG_INFO, "auth_logout: server confirmed logout");
98 7 : return 0;
99 : }
100 :
101 : /* ---- auth_logout ---- */
102 :
103 6 : void auth_logout(const ApiConfig *cfg, MtProtoSession *s, Transport *t) {
104 6 : if (auth_logout_rpc(cfg, s, t) != 0) {
105 2 : logger_log(LOG_WARN,
106 : "auth_logout: server invalidation failed; clearing local session anyway");
107 : }
108 6 : session_store_clear();
109 : /* Drop session-scoped caches. Infrastructure cannot depend on the
110 : * domain layer directly, so we do this through a registered callback
111 : * set by the calling binary. */
112 6 : if (s_on_logout_cb) s_on_logout_cb();
113 6 : }
|