Coverage Report

Created: 2024-01-26 01:52

/work/toxcore/group_pack.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: GPL-3.0-or-later
2
 * Copyright © 2016-2020 The TokTok team.
3
 * Copyright © 2015 Tox project.
4
 */
5
6
/**
7
 * Packer and unpacker functions for saving and loading groups.
8
 */
9
10
#include "group_pack.h"
11
12
#include <stdint.h>
13
#include <stdlib.h>
14
#include <string.h>
15
16
#include "DHT.h"
17
#include "bin_pack.h"
18
#include "bin_unpack.h"
19
#include "ccompat.h"
20
#include "group_common.h"
21
#include "group_moderation.h"
22
#include "logger.h"
23
#include "util.h"
24
25
bool group_privacy_state_from_int(uint8_t value, Group_Privacy_State *out)
26
2.87k
{
27
2.87k
    switch (value) {
28
1.37k
        case GI_PUBLIC: {
29
1.37k
            *out = GI_PUBLIC;
30
1.37k
            return true;
31
0
        }
32
33
246
        case GI_PRIVATE: {
34
246
            *out = GI_PRIVATE;
35
246
            return true;
36
0
        }
37
38
1.25k
        default: {
39
1.25k
            *out = GI_PUBLIC;
40
1.25k
            return false;
41
0
        }
42
2.87k
    }
43
2.87k
}
44
45
bool group_voice_state_from_int(uint8_t value, Group_Voice_State *out)
46
2.87k
{
47
2.87k
    switch (value) {
48
1.25k
        case GV_ALL: {
49
1.25k
            *out = GV_ALL;
50
1.25k
            return true;
51
0
        }
52
53
30
        case GV_MODS: {
54
30
            *out = GV_MODS;
55
30
            return true;
56
0
        }
57
58
5
        case GV_FOUNDER: {
59
5
            *out = GV_FOUNDER;
60
5
            return true;
61
0
        }
62
63
1.58k
        default: {
64
1.58k
            *out = GV_ALL;
65
1.58k
            return false;
66
0
        }
67
2.87k
    }
68
2.87k
}
69
70
non_null()
71
static bool load_unpack_state_values(GC_Chat *chat, Bin_Unpack *bu)
72
11.8k
{
73
11.8k
    if (!bin_unpack_array_fixed(bu, 8, nullptr)) {
74
101
        LOGGER_ERROR(chat->log, "Group state values array malformed");
75
101
        return false;
76
101
    }
77
78
11.7k
    bool manually_disconnected = false;
79
11.7k
    uint8_t privacy_state = 0;
80
11.7k
    uint8_t voice_state = 0;
81
82
11.7k
    if (!(bin_unpack_bool(bu, &manually_disconnected)
83
11.7k
            && bin_unpack_u16(bu, &chat->shared_state.group_name_len)
84
11.7k
            && bin_unpack_u08(bu, &privacy_state)
85
11.7k
            && bin_unpack_u16(bu, &chat->shared_state.maxpeers)
86
11.7k
            && bin_unpack_u16(bu, &chat->shared_state.password_length)
87
11.7k
            && bin_unpack_u32(bu, &chat->shared_state.version)
88
11.7k
            && bin_unpack_u32(bu, &chat->shared_state.topic_lock)
89
11.7k
            && bin_unpack_u08(bu, &voice_state))) {
90
9.26k
        LOGGER_ERROR(chat->log, "Failed to unpack state value");
91
9.26k
        return false;
92
9.26k
    }
93
94
2.49k
    chat->connection_state = manually_disconnected ? CS_DISCONNECTED : CS_CONNECTING;
95
2.49k
    group_privacy_state_from_int(privacy_state, &chat->shared_state.privacy_state);
96
2.49k
    group_voice_state_from_int(voice_state, &chat->shared_state.voice_state);
97
98
    // we always load saved groups as private in case the group became private while we were offline.
99
    // this will have no detrimental effect if the group is public, as the correct privacy
100
    // state will be set via sync.
101
2.49k
    chat->join_type = HJ_PRIVATE;
102
103
2.49k
    return true;
104
11.7k
}
105
106
non_null()
107
static bool load_unpack_state_bin(GC_Chat *chat, Bin_Unpack *bu)
108
2.49k
{
109
2.49k
    if (!bin_unpack_array_fixed(bu, 5, nullptr)) {
110
469
        LOGGER_ERROR(chat->log, "Group state binary array malformed");
111
469
        return false;
112
469
    }
113
114
2.02k
    if (!bin_unpack_bin_fixed(bu, chat->shared_state_sig, SIGNATURE_SIZE)) {
115
1.48k
        LOGGER_ERROR(chat->log, "Failed to unpack shared state signature");
116
1.48k
        return false;
117
1.48k
    }
118
119
543
    if (!bin_unpack_bin_fixed(bu, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY_SIZE)) {
120
19
        LOGGER_ERROR(chat->log, "Failed to unpack founder public key");
121
19
        return false;
122
19
    }
123
124
524
    if (!(bin_unpack_bin_max(bu, chat->shared_state.group_name, &chat->shared_state.group_name_len, sizeof(chat->shared_state.group_name))
125
524
            && bin_unpack_bin_max(bu, chat->shared_state.password, &chat->shared_state.password_length, sizeof(chat->shared_state.password))
126
524
            && bin_unpack_bin_fixed(bu, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE))) {
127
77
        LOGGER_ERROR(chat->log, "Failed to unpack state binary data");
128
77
        return false;
129
77
    }
130
131
447
    return true;
132
524
}
133
134
non_null()
135
static bool load_unpack_topic_info(GC_Chat *chat, Bin_Unpack *bu)
136
447
{
137
447
    if (!bin_unpack_array_fixed(bu, 6, nullptr)) {
138
18
        LOGGER_ERROR(chat->log, "Group topic array malformed");
139
18
        return false;
140
18
    }
141
142
429
    if (!(bin_unpack_u32(bu, &chat->topic_info.version)
143
429
            && bin_unpack_u16(bu, &chat->topic_info.length)
144
429
            && bin_unpack_u16(bu, &chat->topic_info.checksum)
145
429
            && bin_unpack_bin_max(bu, chat->topic_info.topic, &chat->topic_info.length, sizeof(chat->topic_info.topic))
146
429
            && bin_unpack_bin_fixed(bu, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE)
147
429
            && bin_unpack_bin_fixed(bu, chat->topic_sig, SIGNATURE_SIZE))) {
148
70
        LOGGER_ERROR(chat->log, "Failed to unpack topic info");
149
70
        return false;
150
70
    }
151
152
359
    return true;
153
429
}
154
155
non_null()
156
static bool load_unpack_mod_list(GC_Chat *chat, Bin_Unpack *bu)
157
359
{
158
359
    uint32_t actual_size = 0;
159
359
    if (!bin_unpack_array_fixed(bu, 2, &actual_size)) {
160
12
        LOGGER_ERROR(chat->log, "Group mod list array malformed: %d != 2", actual_size);
161
12
        return false;
162
12
    }
163
164
347
    if (!bin_unpack_u16(bu, &chat->moderation.num_mods)) {
165
44
        LOGGER_ERROR(chat->log, "Failed to unpack mod list value");
166
44
        return false;
167
44
    }
168
169
303
    if (chat->moderation.num_mods == 0) {
170
81
        bin_unpack_nil(bu);
171
81
        return true;
172
81
    }
173
174
222
    if (chat->moderation.num_mods > MOD_MAX_NUM_MODERATORS) {
175
18
        LOGGER_ERROR(chat->log, "moderation count %u exceeds maximum %u", chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS);
176
18
        chat->moderation.num_mods = MOD_MAX_NUM_MODERATORS;
177
18
    }
178
179
222
    uint8_t *packed_mod_list = (uint8_t *)malloc(chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE);
180
181
222
    if (packed_mod_list == nullptr) {
182
0
        LOGGER_ERROR(chat->log, "Failed to allocate memory for packed mod list");
183
0
        return false;
184
0
    }
185
186
222
    const size_t packed_size = chat->moderation.num_mods * MOD_LIST_ENTRY_SIZE;
187
188
222
    if (!bin_unpack_bin_fixed(bu, packed_mod_list, packed_size)) {
189
26
        LOGGER_ERROR(chat->log, "Failed to unpack mod list binary data");
190
26
        free(packed_mod_list);
191
26
        return false;
192
26
    }
193
194
196
    if (mod_list_unpack(&chat->moderation, packed_mod_list, packed_size, chat->moderation.num_mods) == -1) {
195
0
        LOGGER_ERROR(chat->log, "Failed to unpack mod list info");
196
0
        free(packed_mod_list);
197
0
        return false;
198
0
    }
199
200
196
    free(packed_mod_list);
201
202
196
    return true;
203
196
}
204
205
non_null()
206
static bool load_unpack_keys(GC_Chat *chat, Bin_Unpack *bu)
207
277
{
208
277
    if (!bin_unpack_array_fixed(bu, 4, nullptr)) {
209
10
        LOGGER_ERROR(chat->log, "Group keys array malformed");
210
10
        return false;
211
10
    }
212
213
267
    if (!(bin_unpack_bin_fixed(bu, chat->chat_public_key, EXT_PUBLIC_KEY_SIZE)
214
267
            && bin_unpack_bin_fixed(bu, chat->chat_secret_key, EXT_SECRET_KEY_SIZE)
215
267
            && bin_unpack_bin_fixed(bu, chat->self_public_key, EXT_PUBLIC_KEY_SIZE)
216
267
            && bin_unpack_bin_fixed(bu, chat->self_secret_key, EXT_SECRET_KEY_SIZE))) {
217
61
        LOGGER_ERROR(chat->log, "Failed to unpack keys");
218
61
        return false;
219
61
    }
220
221
206
    return true;
222
267
}
223
224
non_null()
225
static bool load_unpack_self_info(GC_Chat *chat, Bin_Unpack *bu)
226
206
{
227
206
    if (!bin_unpack_array_fixed(bu, 4, nullptr)) {
228
12
        LOGGER_ERROR(chat->log, "Group self info array malformed");
229
12
        return false;
230
12
    }
231
232
194
    uint8_t self_nick[MAX_GC_NICK_SIZE];
233
194
    uint16_t self_nick_len = 0;
234
194
    uint8_t self_role = GR_USER;
235
194
    uint8_t self_status = GS_NONE;
236
237
194
    if (!(bin_unpack_u16(bu, &self_nick_len)
238
194
            && bin_unpack_u08(bu, &self_role)
239
194
            && bin_unpack_u08(bu, &self_status))) {
240
12
        LOGGER_ERROR(chat->log, "Failed to unpack self values");
241
12
        return false;
242
12
    }
243
244
182
    if (self_nick_len > MAX_GC_NICK_SIZE) {
245
0
        LOGGER_ERROR(chat->log, "self_nick too big (%u bytes), truncating to %d", self_nick_len, MAX_GC_NICK_SIZE);
246
0
        self_nick_len = MAX_GC_NICK_SIZE;
247
0
    }
248
249
182
    if (!bin_unpack_bin_fixed(bu, self_nick, self_nick_len)) {
250
3
        LOGGER_ERROR(chat->log, "Failed to unpack self nick bytes");
251
3
        return false;
252
3
    }
253
254
    // we have to add ourself before setting self info
255
179
    if (peer_add(chat, nullptr, chat->self_public_key) != 0) {
256
0
        LOGGER_ERROR(chat->log, "Failed to add self to peer list");
257
0
        return false;
258
0
    }
259
260
179
    if (chat->numpeers == 0) {
261
0
        LOGGER_ERROR(chat->log, "Failed to unpack self: numpeers should be > 0");
262
0
        return false;
263
0
    }
264
265
179
    GC_Peer *self = &chat->group[0];
266
267
179
    memcpy(self->gconn.addr.public_key, chat->self_public_key, EXT_PUBLIC_KEY_SIZE);
268
179
    memcpy(self->nick, self_nick, self_nick_len);
269
179
    self->nick_length = self_nick_len;
270
179
    self->role = (Group_Role)self_role;
271
179
    self->status = (Group_Peer_Status)self_status;
272
179
    self->gconn.confirmed = true;
273
274
179
    return true;
275
179
}
276
277
non_null()
278
static bool load_unpack_saved_peers(GC_Chat *chat, Bin_Unpack *bu)
279
179
{
280
179
    if (!bin_unpack_array_fixed(bu, 2, nullptr)) {
281
22
        LOGGER_ERROR(chat->log, "Group saved peers array malformed");
282
22
        return false;
283
22
    }
284
285
    // Saved peers
286
157
    uint16_t saved_peers_size = 0;
287
288
157
    if (!bin_unpack_u16(bu, &saved_peers_size)) {
289
1
        LOGGER_ERROR(chat->log, "Failed to unpack saved peers value");
290
1
        return false;
291
1
    }
292
293
156
    if (saved_peers_size == 0) {
294
13
        bin_unpack_nil(bu);
295
13
        return true;
296
13
    }
297
298
143
    uint8_t *saved_peers = (uint8_t *)malloc(saved_peers_size * GC_SAVED_PEER_SIZE);
299
300
143
    if (saved_peers == nullptr) {
301
1
        LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peer list");
302
1
        return false;
303
1
    }
304
305
142
    if (!bin_unpack_bin_fixed(bu, saved_peers, saved_peers_size)) {
306
17
        LOGGER_ERROR(chat->log, "Failed to unpack saved peers binary data");
307
17
        free(saved_peers);
308
17
        return false;
309
17
    }
310
311
125
    if (unpack_gc_saved_peers(chat, saved_peers, saved_peers_size) == -1) {
312
106
        LOGGER_ERROR(chat->log, "Failed to unpack saved peers");  // recoverable error
313
106
    }
314
315
125
    free(saved_peers);
316
317
125
    return true;
318
142
}
319
320
bool gc_load_unpack_group(GC_Chat *chat, Bin_Unpack *bu)
321
14.2k
{
322
14.2k
    uint32_t actual_size;
323
14.2k
    if (!bin_unpack_array_fixed(bu, 7, &actual_size)) {
324
2.34k
        LOGGER_ERROR(chat->log, "Group info array malformed: %d != 7", actual_size);
325
2.34k
        return false;
326
2.34k
    }
327
328
11.8k
    return load_unpack_state_values(chat, bu)
329
11.8k
           && load_unpack_state_bin(chat, bu)
330
11.8k
           && load_unpack_topic_info(chat, bu)
331
11.8k
           && load_unpack_mod_list(chat, bu)
332
11.8k
           && load_unpack_keys(chat, bu)
333
11.8k
           && load_unpack_self_info(chat, bu)
334
11.8k
           && load_unpack_saved_peers(chat, bu);
335
14.2k
}
336
337
non_null()
338
static void save_pack_state_values(const GC_Chat *chat, Bin_Pack *bp)
339
569
{
340
569
    bin_pack_array(bp, 8);
341
569
    bin_pack_bool(bp, chat->connection_state == CS_DISCONNECTED); // 1
342
569
    bin_pack_u16(bp, chat->shared_state.group_name_len); // 2
343
569
    bin_pack_u08(bp, chat->shared_state.privacy_state); // 3
344
569
    bin_pack_u16(bp, chat->shared_state.maxpeers); // 4
345
569
    bin_pack_u16(bp, chat->shared_state.password_length); // 5
346
569
    bin_pack_u32(bp, chat->shared_state.version); // 6
347
569
    bin_pack_u32(bp, chat->shared_state.topic_lock); // 7
348
569
    bin_pack_u08(bp, chat->shared_state.voice_state); // 8
349
569
}
350
351
non_null()
352
static void save_pack_state_bin(const GC_Chat *chat, Bin_Pack *bp)
353
569
{
354
569
    bin_pack_array(bp, 5);
355
356
569
    bin_pack_bin(bp, chat->shared_state_sig, SIGNATURE_SIZE); // 1
357
569
    bin_pack_bin(bp, chat->shared_state.founder_public_key, EXT_PUBLIC_KEY_SIZE); // 2
358
569
    bin_pack_bin(bp, chat->shared_state.group_name, chat->shared_state.group_name_len); // 3
359
569
    bin_pack_bin(bp, chat->shared_state.password, chat->shared_state.password_length); // 4
360
569
    bin_pack_bin(bp, chat->shared_state.mod_list_hash, MOD_MODERATION_HASH_SIZE); // 5
361
569
}
362
363
non_null()
364
static void save_pack_topic_info(const GC_Chat *chat, Bin_Pack *bp)
365
569
{
366
569
    bin_pack_array(bp, 6);
367
368
569
    bin_pack_u32(bp, chat->topic_info.version); // 1
369
569
    bin_pack_u16(bp, chat->topic_info.length); // 2
370
569
    bin_pack_u16(bp, chat->topic_info.checksum); // 3
371
569
    bin_pack_bin(bp, chat->topic_info.topic, chat->topic_info.length); // 4
372
569
    bin_pack_bin(bp, chat->topic_info.public_sig_key, SIG_PUBLIC_KEY_SIZE); // 5
373
569
    bin_pack_bin(bp, chat->topic_sig, SIGNATURE_SIZE); // 6
374
569
}
375
376
non_null()
377
static void save_pack_mod_list(const GC_Chat *chat, Bin_Pack *bp)
378
569
{
379
569
    bin_pack_array(bp, 2);
380
381
569
    const uint16_t num_mods = min_u16(chat->moderation.num_mods, MOD_MAX_NUM_MODERATORS);
382
383
569
    if (num_mods == 0) {
384
165
        bin_pack_u16(bp, num_mods); // 1
385
165
        bin_pack_nil(bp); // 2
386
165
        return;
387
165
    }
388
389
404
    uint8_t *packed_mod_list = (uint8_t *)malloc(num_mods * MOD_LIST_ENTRY_SIZE);
390
391
    // we can still recover without the mod list
392
404
    if (packed_mod_list == nullptr) {
393
0
        bin_pack_u16(bp, 0); // 1
394
0
        bin_pack_nil(bp); // 2
395
0
        LOGGER_ERROR(chat->log, "Failed to allocate memory for moderation list");
396
0
        return;
397
0
    }
398
399
404
    bin_pack_u16(bp, num_mods); // 1
400
401
404
    mod_list_pack(&chat->moderation, packed_mod_list);
402
403
404
    const size_t packed_size = num_mods * MOD_LIST_ENTRY_SIZE;
404
405
404
    bin_pack_bin(bp, packed_mod_list, packed_size); // 2
406
407
404
    free(packed_mod_list);
408
404
}
409
410
non_null()
411
static void save_pack_keys(const GC_Chat *chat, Bin_Pack *bp)
412
569
{
413
569
    bin_pack_array(bp, 4);
414
415
569
    bin_pack_bin(bp, chat->chat_public_key, EXT_PUBLIC_KEY_SIZE); // 1
416
569
    bin_pack_bin(bp, chat->chat_secret_key, EXT_SECRET_KEY_SIZE); // 2
417
569
    bin_pack_bin(bp, chat->self_public_key, EXT_PUBLIC_KEY_SIZE); // 3
418
569
    bin_pack_bin(bp, chat->self_secret_key, EXT_SECRET_KEY_SIZE); // 4
419
569
}
420
421
non_null()
422
static void save_pack_self_info(const GC_Chat *chat, Bin_Pack *bp)
423
569
{
424
569
    bin_pack_array(bp, 4);
425
426
569
    GC_Peer *self = &chat->group[0];
427
428
569
    if (self->nick_length > MAX_GC_NICK_SIZE) {
429
0
        LOGGER_ERROR(chat->log, "self_nick is too big (%u). Truncating to %d", self->nick_length, MAX_GC_NICK_SIZE);
430
0
        self->nick_length = MAX_GC_NICK_SIZE;
431
0
    }
432
433
569
    bin_pack_u16(bp, self->nick_length); // 1
434
569
    bin_pack_u08(bp, (uint8_t)self->role); // 2
435
569
    bin_pack_u08(bp, self->status); // 3
436
569
    bin_pack_bin(bp, self->nick, self->nick_length); // 4
437
569
}
438
439
non_null()
440
static void save_pack_saved_peers(const GC_Chat *chat, Bin_Pack *bp)
441
569
{
442
569
    bin_pack_array(bp, 2);
443
444
569
    uint8_t *saved_peers = (uint8_t *)malloc(GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE);
445
446
    // we can still recover without the saved peers list
447
569
    if (saved_peers == nullptr) {
448
4
        bin_pack_u16(bp, 0); // 1
449
4
        bin_pack_nil(bp); // 2
450
4
        LOGGER_ERROR(chat->log, "Failed to allocate memory for saved peers list");
451
4
        return;
452
4
    }
453
454
565
    uint16_t packed_size = 0;
455
565
    const int count = pack_gc_saved_peers(chat, saved_peers, GC_MAX_SAVED_PEERS * GC_SAVED_PEER_SIZE, &packed_size);
456
457
565
    if (count < 0) {
458
0
        LOGGER_ERROR(chat->log, "Failed to pack saved peers");
459
0
    }
460
461
565
    bin_pack_u16(bp, packed_size); // 1
462
463
565
    if (packed_size == 0) {
464
124
        bin_pack_nil(bp); // 2
465
124
        free(saved_peers);
466
124
        return;
467
124
    }
468
469
441
    bin_pack_bin(bp, saved_peers, packed_size); // 2
470
471
441
    free(saved_peers);
472
441
}
473
474
void gc_save_pack_group(const GC_Chat *chat, Bin_Pack *bp)
475
2.64k
{
476
2.64k
    if (chat->numpeers == 0) {
477
2.08k
        LOGGER_ERROR(chat->log, "Failed to pack group: numpeers is 0");
478
2.08k
        return;
479
2.08k
    }
480
481
569
    bin_pack_array(bp, 7);
482
483
569
    save_pack_state_values(chat, bp); // 1
484
569
    save_pack_state_bin(chat, bp); // 2
485
569
    save_pack_topic_info(chat, bp); // 3
486
569
    save_pack_mod_list(chat, bp); // 4
487
569
    save_pack_keys(chat, bp); // 5
488
569
    save_pack_self_info(chat, bp); // 6
489
569
    save_pack_saved_peers(chat, bp); // 7
490
569
}