Skip to content

Commit

Permalink
rawx: check chunk file size before answering HEAD or GET
Browse files Browse the repository at this point in the history
  • Loading branch information
fvennetier committed Jun 21, 2022
1 parent b2711f8 commit e94cc05
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 4 deletions.
31 changes: 30 additions & 1 deletion rawx-apache2/src/rawx_repo_core.c
Expand Up @@ -2,6 +2,7 @@
OpenIO SDS rawx-apache2
Copyright (C) 2014 Worldline, as part of Redcurrant
Copyright (C) 2015-2019 OpenIO SAS, as part of OpenIO SDS
Copyright (C) 2022 OVH SAS
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
Expand Down Expand Up @@ -454,7 +455,7 @@ chunk_verify_checksum(dav_resource *resource, request_rec *r)
APR_FOPEN_BINARY,
0, resource->pool);
if (status != APR_SUCCESS) {
/* It should be already catched by resource_stat_chunk */
/* It should be already caught by resource_stat_chunk */
return status;
}

Expand Down Expand Up @@ -494,6 +495,34 @@ chunk_verify_checksum(dav_resource *resource, request_rec *r)
return code;
}

dav_error*
chunk_verify_size(const dav_resource *resource, request_rec *req)
{
if (!resource->info->chunk.chunk_size) {
DAV_ERROR_RES(resource, 0,
"No chunk_size attr on %s, cannot compare to actual size",
resource->info->fullpath);
return NULL;
} else if (resource->info->compression) {
DAV_DEBUG_RES(resource, 0,
"Chunk %s is compressed, won't compare attr and actual size",
resource->info->fullpath);
return NULL;
}

apr_int64_t chunk_size = apr_strtoi64(resource->info->chunk.chunk_size, NULL, 10);

if (resource->info->finfo.size != chunk_size) {
char *err_msg = apr_psprintf(req->pool,
"File size (%ld) different from expected chunk size (%ld)",
resource->info->finfo.size, chunk_size);
return server_create_and_stat_error(
request_get_server_config(req), req->pool,
HTTP_PRECONDITION_FAILED, 0, err_msg);
}
return NULL;
}

apr_status_t
resource_stat_chunk(dav_resource *resource, int flags)
{
Expand Down
5 changes: 5 additions & 0 deletions rawx-apache2/src/rawx_repo_core.h
Expand Up @@ -2,6 +2,7 @@
OpenIO SDS rawx-apache2
Copyright (C) 2014 Worldline, as part of Redcurrant
Copyright (C) 2015-2019 OpenIO SAS, as part of OpenIO SDS
Copyright (C) 2022 OVH SAS
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
Expand Down Expand Up @@ -106,6 +107,10 @@ const char * check_chunk_info_with_trailers(apr_pool_t *pool,

apr_status_t chunk_verify_checksum(dav_resource *resource, request_rec *r);

/* Verify the file size matches the expected chunk size.
* Returns NULL if there is no problem. */
dav_error* chunk_verify_size(const dav_resource *resource, request_rec *req);

void request_parse_query(request_rec *r, dav_resource *resource);

void request_fill_headers(request_rec *r, struct chunk_textinfo_s *chunk);
Expand Down
16 changes: 16 additions & 0 deletions rawx-apache2/src/rawx_repository.c
Expand Up @@ -2,6 +2,7 @@
OpenIO SDS rawx-apache2
Copyright (C) 2014 Worldline, as part of Redcurrant
Copyright (C) 2015-2019 OpenIO SAS, as part of OpenIO SDS
Copyright (C) 2022 OVH SAS
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
Expand Down Expand Up @@ -477,6 +478,14 @@ dav_rawx_get_resource(request_rec *r, const char *root_dir, const char *label,
apr_strerror(status, buff, sizeof(buff)), NULL));
}

if (resource->exists && r->header_only) {
/* This is a HEAD request, check the file size. */
e = chunk_verify_size(resource, r);
if (e != NULL) {
return e;
}
}

if (resource->exists && (flags & RESOURCE_STAT_CHUNK_CHECK_HASH)) {
EXTRA_ASSERT(!(flags & RESOURCE_STAT_CHUNK_PENDING));

Expand Down Expand Up @@ -853,6 +862,13 @@ dav_rawx_deliver(const dav_resource *resource, ap_filter_t *output)

if (!ctx->compression){
apr_file_t *fd = NULL;
apr_int64_t chunk_size = apr_strtoi64(ctx->chunk.chunk_size, NULL, 10);

if (resource->info->finfo.size != chunk_size) {
e = chunk_verify_size(resource, ctx->request);
if (e != NULL)
goto end_deliver;
}

/* Try to open the file but forbids a creation */
status = apr_file_open(&fd, resource_get_pathname(resource),
Expand Down
24 changes: 23 additions & 1 deletion rawx/handler_chunk.go
@@ -1,6 +1,6 @@
// OpenIO SDS Go rawx
// Copyright (C) 2015-2020 OpenIO SAS
// Copyright (C) 2021 OVH SAS
// Copyright (C) 2021-2022 OVH SAS
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Affero General Public
Expand Down Expand Up @@ -276,6 +276,15 @@ func (rr *rawxRequest) copyChunk() {
}
}

func (rr *rawxRequest) checkChunkSize(chunkIn fileReader) (error) {
if rr.chunk.size != chunkIn.size() && rr.chunk.compression == "off" {
return errors.New(fmt.Sprintf(
"File size (%d) different from recorded chunk size (%d)",
chunkIn.size(), rr.chunk.size))
}
return nil
}

func (rr *rawxRequest) checkChunk() {
chunkIn, err := rr.rawx.repo.get(rr.chunkID)
if err != nil {
Expand All @@ -297,6 +306,12 @@ func (rr *rawxRequest) checkChunk() {
return
}

err = rr.checkChunkSize(chunkIn)
if err != nil {
rr.replyCode(http.StatusPreconditionFailed)
return
}

if GetBool(rr.req.Header.Get(HeaderNameCheckHash), false) {
expected_hash := rr.req.Header.Get(HeaderNameChunkChecksum)
if expected_hash == "" {
Expand Down Expand Up @@ -379,6 +394,13 @@ func (rr *rawxRequest) downloadChunk() {
return
}

err = rr.checkChunkSize(inChunk)
if err != nil {
LogWarning("Won't serve chunk %s: %v", rr.chunkID, err)
rr.replyCode(http.StatusPreconditionFailed)
return
}

var rangeInf rangeInfo
// A potential decompression filter
var filter io.ReadCloser
Expand Down
5 changes: 3 additions & 2 deletions tests/functional/blob/test_blob.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-

# Copyright (C) 2015-2019 OpenIO SAS, as part of OpenIO SDS
# Copyright (C) 2022 OVH SAS
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -898,11 +899,11 @@ def test_HEAD_chunk(self):
resp, body = self._http_request(
chunkurl, 'HEAD', '',
{'x-oio-check-hash': True,
'x-oio-chunk-meta-chunk-hash': 'A'*32})
'x-oio-chunk-meta-chunk-hash': 'A' * 32})
self.assertEqual(412, resp.status)

# Corrupt the chunk
corrupted_data = 'chunk is dead'
corrupted_data = 'x' * length
with open(self._chunk_path(chunkid), "wb") as fp:
fp.write(corrupted_data)

Expand Down

0 comments on commit e94cc05

Please sign in to comment.