diff --git a/src/http.c b/src/http.c index e6af7c16..4466b31d 100644 --- a/src/http.c +++ b/src/http.c @@ -2894,10 +2894,18 @@ metalink_from_http (const struct response *resp, const struct http_stat *hs, Therefore we convert: base64 -> binary -> hex. */ const size_t dig_hash_str_len = strlen (dig_hash); char *bin_hash = alloca (dig_hash_str_len * 3 / 4 + 1); - size_t hash_bin_len; + ssize_t hash_bin_len; hash_bin_len = base64_decode (dig_hash, bin_hash); + /* Detect malformed base64 input. */ + if (hash_bin_len < 0) + { + xfree (dig_type); + xfree (dig_hash); + continue; + } + /* One slot for me, one for zero-termination. */ mfile->checksums = xrealloc (mfile->checksums, @@ -2905,8 +2913,8 @@ metalink_from_http (const struct response *resp, const struct http_stat *hs, mfile->checksums[hash_count] = xnew (metalink_checksum_t); mfile->checksums[hash_count]->type = dig_type; - mfile->checksums[hash_count]->hash = xmalloc (hash_bin_len * 2 + 1); - wg_hex_to_string (mfile->checksums[hash_count]->hash, bin_hash, hash_bin_len); + mfile->checksums[hash_count]->hash = xmalloc ((size_t)hash_bin_len * 2 + 1); + wg_hex_to_string (mfile->checksums[hash_count]->hash, bin_hash, (size_t)hash_bin_len); xfree (dig_hash); diff --git a/testenv/Makefile.am b/testenv/Makefile.am index daba609b..ff9fe059 100644 --- a/testenv/Makefile.am +++ b/testenv/Makefile.am @@ -29,6 +29,7 @@ if METALINK_IS_ENABLED METALINK_TESTS = Test-metalink-http.py \ Test-metalink-http-quoted.py \ + Test-metalink-http-baddigest.py \ Test-metalink-http-xml.py \ Test-metalink-http-xml-trust.py \ Test-metalink-xml.py \ diff --git a/testenv/Test-metalink-http-baddigest.py b/testenv/Test-metalink-http-baddigest.py new file mode 100755 index 00000000..2496da76 --- /dev/null +++ b/testenv/Test-metalink-http-baddigest.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +from sys import exit +from test.http_test import HTTPTest +from misc.wget_file import WgetFile +import hashlib +from base64 import b64encode + +""" + This is to test Metalink/HTTP with a malformed base64 Digest header. + + With --trust-server-names, trust the metalink:file names. + + Without --trust-server-names, don't trust the metalink:file names: + use the basename of --input-metalink, and add a sequential number + (e.g. .#1, .#2, etc.). + + Strip the directory from unsafe paths. +""" + +############# File Definitions ############################################### +bad = "Ouch!" +bad_sha256 = b64encode (hashlib.sha256 (bad.encode ('UTF-8')).digest ()).decode ('ascii') + +LinkHeaders = ["; rel=duplicate; pri=1"] +DigestHeader = "SHA-256=bad_base64,SHA-256={{BAD_HASH}}" + +# This will be filled as soon as we know server hostname and port +MetaHTTPRules = {'SendHeader' : {}} + +MetaHTTP = WgetFile ("main.metalink", rules=MetaHTTPRules) + +wrong_file = WgetFile ("wrong_file", bad) +wrong_file_down = WgetFile ("main.metalink", bad) + +WGET_OPTIONS = "--metalink-over-http" +WGET_URLS = [["main.metalink"]] + +RequestList = [[ + "HEAD /main.metalink", + "GET /wrong_file" +]] + +Files = [[ + MetaHTTP, + wrong_file +]] +Existing_Files = [] + +ExpectedReturnCode = 0 +ExpectedDownloadedFiles = [wrong_file_down] + +################ Pre and Post Test Hooks ##################################### +pre_test = { + "ServerFiles" : Files, + "LocalFiles" : Existing_Files +} +test_options = { + "WgetCommands" : WGET_OPTIONS, + "Urls" : WGET_URLS +} +post_test = { + "ExpectedFiles" : ExpectedDownloadedFiles, + "ExpectedRetcode" : ExpectedReturnCode, + "FilesCrawled" : RequestList +} + +http_test = HTTPTest ( + pre_hook=pre_test, + test_params=test_options, + post_hook=post_test +) + +http_test.server_setup() +### Get and use dynamic server sockname +srv_host, srv_port = http_test.servers[0].server_inst.socket.getsockname () + +DigestHeader = DigestHeader.replace('{{BAD_HASH}}', bad_sha256) + +# Helper function for hostname, port and digest substitution +def SubstituteServerInfo (text, host, port): + text = text.replace('{{SRV_HOST}}', host) + text = text.replace('{{SRV_PORT}}', str (port)) + return text + +MetaHTTPRules["SendHeader"] = { + 'Link': [ SubstituteServerInfo (LinkHeader, srv_host, srv_port) + for LinkHeader in LinkHeaders ], + 'Digest': DigestHeader +} + +err = http_test.begin () + +exit (err)