Browse Source

Protect avatar paths across platforms and initialize join fixture nodes

master
evgeny 6 days ago
parent
commit
80c81502fb
  1. 13
      src/member_files/storage.c
  2. 5
      tests/test_chat_join_e2e.c
  3. 2
      tests/test_member_files.c

13
src/member_files/storage.c

@ -5,6 +5,7 @@
#include "../../lib/u_async.h"
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
@ -14,6 +15,13 @@
#define RECEIPT_TB 1200000ULL
#define STORE_USERS 256
// Windows сравнивает имена без регистра; алиас не должен обходить неизменяемость avatars.
static int store_avatar_namespace(const char* path) {
const char name[] = "avatars";
for (unsigned i = 0; i < sizeof(name) - 1; i++) if (tolower((unsigned char)path[i]) != name[i]) return 0;
return !path[7] || path[7] == '/';
}
struct store_file { char path[MF_PATH_MAX + 80]; int fd; uint64_t touched; };
struct store_user { struct store_user* next; uint64_t owner, used, reserved, files, directories; int valid; };
struct store_upload {
@ -383,8 +391,9 @@ static int store_begin(struct mf_storage* s, uint64_t group, uint64_t peer, cons
static int store_command(struct mf_storage* s, uint64_t group, uint64_t peer, const struct mf_message* q, struct mf_message* r) {
if (!mf_path_valid(q->path, q->command == MF_LIST || q->command == MF_STAT || q->command == MF_QUOTA) ||
(q->command == MF_RENAME && !mf_path_valid(q->target, 0))) return MF_INVALID_PATH;
if (q->command == MF_RENAME && (!strcmp(q->path, "avatars") || !strncmp(q->path, "avatars/", 8) ||
!strcmp(q->target, "avatars") || !strncmp(q->target, "avatars/", 8))) return MF_INVALID_PATH;
if (store_avatar_namespace(q->path) && strcmp(q->path, "avatars") &&
(strncmp(q->path, "avatars/", 8) || !mf_avatar_file_valid(q->path + 8))) return MF_INVALID_PATH;
if (q->command == MF_RENAME && (store_avatar_namespace(q->path) || store_avatar_namespace(q->target))) return MF_INVALID_PATH;
char path[MF_PATH_MAX + 80];
snprintf(path, sizeof(path), "%llu/%llu%s%s", (unsigned long long)group, (unsigned long long)q->owner, *q->path ? "/" : "", q->path);
struct mf_disk_info info = {0};

5
tests/test_chat_join_e2e.c

@ -308,7 +308,10 @@ static void member_put_self(struct UTUN_INSTANCE* inst, const char* name) {
}
static void member_put_placeholder(struct UTUN_INSTANCE* inst, int idx, const char* name) {
(void)name;
/* Ключи fixture известны родителю; плейсхолдер не создаёт общую запись узла сам. */
if (topo_node_sqlite_node_update_verified(inst->topo_sqlite_db, g_sh.nid[idx], name, g_sh.x_pub[idx], g_sh.ed_pub[idx],
(uint64_t)ntp_time_get_seconds(inst), 0) < 0)
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "test: cannot initialize fixture node=%016llx", (unsigned long long)g_sh.nid[idx]);
if (topo_node_sqlite_member_placeholder_put(inst->topo_sqlite_db, g_sh.ch_id, g_sh.nid[idx], g_sh.x_pub[idx], g_sh.ed_pub[idx]) < 0)
DEBUG_ERROR(DEBUG_CATEGORY_MEMBER_SYNC, "test: cannot create member placeholder node=%016llx", (unsigned long long)g_sh.nid[idx]);
}

2
tests/test_member_files.c

@ -54,6 +54,8 @@ static void avatar_storage_test(const char* directory) {
q.size = MF_AVATAR_MAX_BYTES + 1; run(s, 55, 77, MF_BEGIN_WRITE, MF_INVALID_RANGE);
q.size = 0; run(s, 55, 77, MF_BEGIN_WRITE, MF_INVALID_RANGE);
memcpy(q.version, version, 16); snprintf(q.target, sizeof(q.target), "renamed.png"); run(s, 55, 77, MF_RENAME, MF_INVALID_PATH);
snprintf(q.path, sizeof(q.path), "Avatars/%s.png", hash); run(s, 55, 77, MF_BEGIN_WRITE, MF_INVALID_PATH);
snprintf(q.path, sizeof(q.path), "AVATARS"); run(s, 55, 77, MF_MKDIR, MF_INVALID_PATH);
memset(&q, 0, sizeof(q)); q.owner = 77; run(s, 55, 88, MF_QUOTA, MF_OK);
EXPECT(mf_get64(r.data) == sizeof(contents) && mf_get64(r.data + 16) == 0 &&
mf_get64(r.data + 24) == 1 && mf_get64(r.data + 32) == 1, "avatars counted once in UID quota");

Loading…
Cancel
Save