Browse Source

Bound avatar transfers and enforce immutable hashed uploads

master
evgeny 1 week ago
parent
commit
e8a3d5fa16
  1. 1
      src/member_files/member_files.h
  2. 8
      src/member_files/storage.c
  3. 8
      src/member_files/transfer.c

1
src/member_files/member_files.h

@ -30,6 +30,7 @@ void member_files_gui_request(void* arg);
struct mf_transfer_request {
struct UTUN_INSTANCE* inst;
uint64_t id, group, server, owner;
uint64_t max_bytes; // 0 — без лимита; проверяется до открытия локального файла и передачи данных.
int upload;
uint8_t version[16];
char remote[MF_PATH_MAX + 1], local[MF_PATH_MAX + 1];

8
src/member_files/storage.c

@ -317,6 +317,12 @@ static struct store_upload* store_upload_find(struct mf_storage* s, uint64_t gro
static int store_begin(struct mf_storage* s, uint64_t group, uint64_t peer, const char* path, const struct mf_message* q) {
if (q->size > INT64_MAX || mf_zero(q->upload, 16)) return MF_INVALID_RANGE;
if (!strncmp(q->path, "avatars/", 8)) {
if (!mf_avatar_file_valid(q->path + 8) || !q->size || q->size > MF_AVATAR_MAX_BYTES) return MF_INVALID_RANGE;
if (!mf_zero(q->version, 16)) return MF_EXISTS;
char hash[65]; for (unsigned i = 0; i < 32; i++) snprintf(hash + i * 2, 3, "%02x", q->hash[i]);
if (strncmp(q->path + 8, hash, 64)) return MF_HASH_MISMATCH;
}
struct store_upload* u = store_upload_find(s, group, peer, q->upload);
if (u) {
if (strcmp(u->path, path) || u->size != q->size || memcmp(u->hash, q->hash, 32) || memcmp(u->expected, q->version, 16))
@ -377,6 +383,8 @@ 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;
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};

8
src/member_files/transfer.c

@ -125,7 +125,8 @@ static void transfer_work(void* arg) {
} else if (t->sending) {
rc = mf_disk_local_open(t->request.local, 0, &t->fd);
if (!rc) rc = mf_disk_info(t->fd, &t->source);
if (!rc && (t->source.type != MF_FILE || t->source.size > INT64_MAX)) rc = MF_INVALID_RANGE;
if (!rc && (t->source.type != MF_FILE || t->source.size > INT64_MAX ||
(t->request.max_bytes && t->source.size > t->request.max_bytes))) rc = MF_INVALID_RANGE;
if (!rc) { t->io_size = t->source.size; rc = mf_disk_hash(t->fd, t->io_size, t->hash); }
struct mf_disk_info after;
if (!rc) rc = mf_disk_info(t->fd, &after);
@ -345,6 +346,11 @@ static void transfer_response(void* arg, const struct mf_message* r) {
if (r->status) { transfer_stop(t, r->status); return; }
if (r->command == MF_STAT) {
if (r->type != MF_FILE || r->size > INT64_MAX || mf_zero(r->version, 16)) { transfer_stop(t, MF_INVALID_RANGE); return; }
if (t->request.max_bytes && r->size > t->request.max_bytes) {
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: download exceeds limit id=%llu bytes=%llu limit=%llu",
(unsigned long long)t->request.id, (unsigned long long)r->size, (unsigned long long)t->request.max_bytes);
transfer_stop(t, MF_INVALID_RANGE); return;
}
t->size = r->size; memcpy(t->version, r->version, 16); transfer_submit(t, TRANSFER_PREPARE);
} else if (r->size != t->size || r->cursor != t->request.id || memcmp(r->upload, t->upload, 16)) {
transfer_stop(t, MF_BAD_PACKET);

Loading…
Cancel
Save