diff --git a/include/dogecoin/bip39.h b/include/dogecoin/bip39.h index 8aa993f82..22acaa930 100644 --- a/include/dogecoin/bip39.h +++ b/include/dogecoin/bip39.h @@ -173,6 +173,11 @@ int verify_mnemonic_sentence(const char* mnemonic, const char* wordlist[], const /* size of the generated mnemonic in bytes (output) */ /* generated mnemonic (output) */ /* returns 0 (success), -1 (fail) */ +/* entropy_out receives the entropy as a hex string and must have room for + MAX_ENTROPY_STRING_SIZE bytes -- the type is HEX_ENTROPY. The signature says + char*, so the requirement is not visible at the call site; entropy_size is + validated to 128..256 bits, which bounds the write to 64 hex characters plus + a terminator. */ LIBDOGECOIN_API int dogecoin_generate_mnemonic (const ENTROPY_SIZE entropy_size, const char* language, const char* space, const char* entropy, const char* filename, char* entropy_out, size_t* size, char* words); /* Verifies the mnemonic phrase */ diff --git a/include/dogecoin/utils.h b/include/dogecoin/utils.h index da2d0fdf7..5ea5de118 100644 --- a/include/dogecoin/utils.h +++ b/include/dogecoin/utils.h @@ -73,6 +73,10 @@ LIBDOGECOIN_API char* to_string(uint8_t* x); LIBDOGECOIN_API char* hash_to_string(uint8_t* x); LIBDOGECOIN_API uint8_t* hash_to_bytes(uint8_t* x); LIBDOGECOIN_API void* safe_malloc(size_t size); +/* Fast, NON-CRYPTOGRAPHIC randomness, seeded from the wall clock. For values + that only need to be unlikely to repeat -- P2P nonces are the in-tree use. + Never for keys, seeds, entropy or anything an attacker benefits from + predicting: use dogecoin_random_bytes(), which fails closed. */ LIBDOGECOIN_API void dogecoin_cheap_random_bytes(uint8_t* buf, size_t len); LIBDOGECOIN_API void dogecoin_get_default_datadir(cstring* path_out); LIBDOGECOIN_API void dogecoin_file_commit(FILE* file); diff --git a/rpctest/fetch.py b/rpctest/fetch.py index 69e2c0607..05235b854 100755 --- a/rpctest/fetch.py +++ b/rpctest/fetch.py @@ -91,7 +91,33 @@ zipfile.extractall(os.getcwd()) else: with tarfile.open(fileobj=BytesIO(req.content), mode='r:gz') as tar: - tar.extractall(os.getcwd()) + # Filter members rather than trusting the archive. Without this a + # crafted tarball can write outside the destination via '../' members, + # absolute paths, or symlinks pointing out of the tree. The checksum + # check above narrows who can supply such an archive, but it does not + # make extraction safe on its own -- and zipfile.extractall (the branch + # above) already sanitises member paths, so only tar was exposed. + if hasattr(tarfile, 'data_filter'): + tar.extractall(os.getcwd(), filter='data') + else: + # PEP 706 filters land in 3.12 and were backported to security + # releases of 3.8+. Where they are unavailable, check by hand + # rather than silently extracting unfiltered. + dest = os.path.abspath(os.getcwd()) + for member in tar.getmembers(): + target = os.path.abspath(os.path.join(dest, member.name)) + if not (target == dest or target.startswith(dest + os.sep)): + print("\033[31m> refusing tar member outside destination: " + + member.name + "\033[0m") + exit(1) + if member.issym() or member.islnk(): + link = os.path.abspath(os.path.join( + os.path.dirname(target), member.linkname)) + if not (link == dest or link.startswith(dest + os.sep)): + print("\033[31m> refusing tar link outside destination: " + + member.name + "\033[0m") + exit(1) + tar.extractall(dest) tar.close() deps_path = ["dogecoind"]