diff --git a/rawx-apache2/src/rawx_repo_core.c b/rawx-apache2/src/rawx_repo_core.c index 8534705af8..30177edcad 100644 --- a/rawx-apache2/src/rawx_repo_core.c +++ b/rawx-apache2/src/rawx_repo_core.c @@ -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 @@ -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; } @@ -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) { diff --git a/rawx-apache2/src/rawx_repo_core.h b/rawx-apache2/src/rawx_repo_core.h index ab8ae5d7fd..977fe53713 100644 --- a/rawx-apache2/src/rawx_repo_core.h +++ b/rawx-apache2/src/rawx_repo_core.h @@ -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 @@ -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); diff --git a/rawx-apache2/src/rawx_repository.c b/rawx-apache2/src/rawx_repository.c index 30314281b9..3e78ac3635 100644 --- a/rawx-apache2/src/rawx_repository.c +++ b/rawx-apache2/src/rawx_repository.c @@ -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 @@ -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)); @@ -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), diff --git a/rawx/handler_chunk.go b/rawx/handler_chunk.go index 87d7b34209..db7fedc206 100644 --- a/rawx/handler_chunk.go +++ b/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 @@ -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 { @@ -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 == "" { @@ -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 diff --git a/tests/functional/blob/test_blob.py b/tests/functional/blob/test_blob.py index fba0de1370..46f75c659d 100644 --- a/tests/functional/blob/test_blob.py +++ b/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 @@ -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)