diff --git a/configure.ac b/configure.ac index bccb0b687b28..b9c5214c9f19 100644 --- a/configure.ac +++ b/configure.ac @@ -251,28 +251,28 @@ AC_ARG_ENABLE(man, enable_man=yes) AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) -# Enable debug +dnl Enable debug AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [use compiler flags and macros suited for debugging (default is no)])], [enable_debug=$enableval], [enable_debug=no]) -# Enable exception stacktraces +dnl Enable exception stacktraces AC_ARG_ENABLE([stacktraces], [AS_HELP_STRING([--enable-stacktraces], [gather and print exception stack traces (default is yes)])], [enable_stacktraces=$enableval], [enable_stacktraces=yes]) -# Enable crash hooks +dnl Enable crash hooks AC_ARG_ENABLE([crash-hooks], [AS_HELP_STRING([--enable-crash-hooks], [hook into exception/signal/assert handling to gather stack traces (default is no)])], [enable_crashhooks=$enableval], [enable_crashhooks=no]) -# Enable in-wallet miner +dnl Enable in-wallet miner AC_ARG_ENABLE([miner], [AS_HELP_STRING([--enable-miner], [enable in-wallet miner (default is yes)])], @@ -283,20 +283,20 @@ if test "x$enable_miner" = xyes; then AC_DEFINE(ENABLE_MINER, 1, [Define this symbol if in-wallet miner should be enabled]) fi -# Enable different -fsanitize options +dnl Enable different -fsanitize options AC_ARG_WITH([sanitizers], [AS_HELP_STRING([--with-sanitizers], [comma separated list of extra sanitizers to build with (default is none enabled)])], [use_sanitizers=$withval]) -# Enable gprof profiling +dnl Enable gprof profiling AC_ARG_ENABLE([gprof], [AS_HELP_STRING([--enable-gprof], [use gprof profiling compiler flags (default is no)])], [enable_gprof=$enableval], [enable_gprof=no]) -# Turn warnings into errors +dnl Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat certain compiler warnings as errors (default is no)])], @@ -307,15 +307,15 @@ AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) if test "x$enable_debug" = xyes; then - # Clear default -g -O2 flags + dnl Clear default -g -O2 flags if test "x$CXXFLAGS_overridden" = xno; then CXXFLAGS="" fi - # Disable all optimizations + dnl Disable all optimizations AX_CHECK_COMPILE_FLAG([-O0], [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]]) - # Prefer -g3, fall back to -g if that is unavailable. + dnl Prefer -g3, fall back to -g if that is unavailable. AX_CHECK_COMPILE_FLAG( [-g3], [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g3"]], @@ -366,19 +366,19 @@ fi AX_CHECK_COMPILE_FLAG([-Wa,-mbig-obj], [CXXFLAGS="$CXXFLAGS -Wa,-mbig-obj"],,,) if test x$use_sanitizers != x; then - # First check if the compiler accepts flags. If an incompatible pair like - # -fsanitize=address,thread is used here, this check will fail. This will also - # fail if a bad argument is passed, e.g. -fsanitize=undfeined + dnl First check if the compiler accepts flags. If an incompatible pair like + dnl -fsanitize=address,thread is used here, this check will fail. This will also + dnl fail if a bad argument is passed, e.g. -fsanitize=undfeined AX_CHECK_COMPILE_FLAG( [[-fsanitize=$use_sanitizers]], [[SANITIZER_CXXFLAGS=-fsanitize=$use_sanitizers]], [AC_MSG_ERROR([compiler did not accept requested flags])]) - # Some compilers (e.g. GCC) require additional libraries like libasan, - # libtsan, libubsan, etc. Make sure linking still works with the sanitize - # flag. This is a separate check so we can give a better error message when - # the sanitize flags are supported by the compiler but the actual sanitizer - # libs are missing. + dnl Some compilers (e.g. GCC) require additional libraries like libasan, + dnl libtsan, libubsan, etc. Make sure linking still works with the sanitize + dnl flag. This is a separate check so we can give a better error message when + dnl the sanitize flags are supported by the compiler but the actual sanitizer + dnl libs are missing. AX_CHECK_LINK_FLAG( [[-fsanitize=$use_sanitizers]], [[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]], @@ -432,9 +432,9 @@ if test "x$CXXFLAGS_overridden" = "xno"; then [AC_LANG_SOURCE([[struct A { virtual void f(); }; struct B : A { void f() final; };]])]) AX_CHECK_COMPILE_FLAG([-Wunreachable-code-loop-increment],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunreachable-code-loop-increment"],,[[$CXXFLAG_WERROR]]) - ## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all - ## unknown options if any other warning is produced. Test the -Wfoo case, and - ## set the -Wno-foo case if it works. + dnl Some compilers (gcc) ignore unknown -Wno-* options, but warn about all + dnl unknown options if any other warning is produced. Test the -Wfoo case, and + dnl set the -Wno-foo case if it works. AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wself-assign],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]]) @@ -640,8 +640,8 @@ case $host in AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(libiphlpapi missing)) AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(libcrypt32 missing)) - # -static is interpreted by libtool, where it has a different meaning. - # In libtool-speak, it's -all-static. + dnl -static is interpreted by libtool, where it has a different meaning. + dnl In libtool-speak, it's -all-static. AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) AC_PATH_PROG([MAKENSIS], [makensis], none) @@ -680,8 +680,8 @@ case $host in dnl It's safe to add these paths even if the functionality is disabled by dnl the user (--without-wallet or --without-gui for example). - bdb_prefix=`$BREW --prefix berkeley-db4 2>/dev/null` - qt5_prefix=`$BREW --prefix qt5 2>/dev/null` + bdb_prefix=$($BREW --prefix berkeley-db4 2>/dev/null) + qt5_prefix=$($BREW --prefix qt5 2>/dev/null) if test x$bdb_prefix != x; then CPPFLAGS="$CPPFLAGS -I$bdb_prefix/include" LIBS="$LIBS -L$bdb_prefix/lib" @@ -801,11 +801,11 @@ AC_C_BIGENDIAN dnl Check for pthread compile/link requirements AX_PTHREAD -# The following macro will add the necessary defines to dash-config.h, but -# they also need to be passed down to any subprojects. Pull the results out of -# the cache and add them to CPPFLAGS. +dnl The following macro will add the necessary defines to dash-config.h, but +dnl they also need to be passed down to any subprojects. Pull the results out of +dnl the cache and add them to CPPFLAGS. AC_SYS_LARGEFILE -# detect POSIX or GNU variant of strerror_r +dnl detect POSIX or GNU variant of strerror_r AC_FUNC_STRERROR_R if test x$ac_cv_sys_file_offset_bits != x && @@ -826,8 +826,8 @@ AX_GCC_FUNC_ATTRIBUTE([dllimport]) if test x$use_glibc_compat != xno; then - #__fdelt_chk's params and return type have changed from long unsigned int to long int. - # See which one is present here. + dnl __fdelt_chk's params and return type have changed from long unsigned int to long int. + dnl See which one is present here. AC_MSG_CHECKING(__fdelt_chk type) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE @@ -869,22 +869,22 @@ if test "x$enable_gprof" = xyes; then fi if test x$TARGET_OS != xwindows; then - # All windows code is PIC, forcing it on just adds useless compile warnings + dnl All windows code is PIC, forcing it on just adds useless compile warnings AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"]) fi -# All versions of gcc that we commonly use for building are subject to bug -# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set -# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) +dnl All versions of gcc that we commonly use for building are subject to bug +dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set +dnl -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) AX_CHECK_COMPILE_FLAG([-fstack-reuse=none],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-reuse=none"]) if test x$use_hardening != xno; then use_hardening=yes AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) - # When enable_debug is yes, all optimizations are disabled. - # However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning. - # Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes. + dnl When enable_debug is yes, all optimizations are disabled. + dnl However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning. + dnl Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes. if test x$enable_debug != xyes; then AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[ AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[ @@ -919,8 +919,8 @@ fi AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h]) -# FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS -# check that it fails to build without memcpy, then that it builds with +dnl FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS +dnl check that it fails to build without memcpy, then that it builds with AC_MSG_CHECKING(FD_ZERO memcpy dependence) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include @@ -958,7 +958,7 @@ AC_CHECK_DECLS([getifaddrs, freeifaddrs],,, ) AC_CHECK_DECLS([strnlen]) -# Check for daemon(3), unrelated to --with-daemon (although used by it) +dnl Check for daemon(3), unrelated to --with-daemon (although used by it) AC_CHECK_DECLS([daemon]) AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, @@ -1024,19 +1024,19 @@ if test "x$use_thread_local" = xyes || { test "x$use_thread_local" = xauto && te [ case $host in *mingw*) - # mingw32's implementation of thread_local has also been shown to behave - # erroneously under concurrent usage; see: - # https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605 + dnl mingw32's implementation of thread_local has also been shown to behave + dnl erroneously under concurrent usage; see: + dnl https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605 AC_MSG_RESULT(no) ;; *darwin*) - # TODO enable thread_local on later versions of Darwin where it is - # supported (per https://stackoverflow.com/a/29929949) + dnl TODO enable thread_local on later versions of Darwin where it is + dnl supported (per https://stackoverflow.com/a/29929949) AC_MSG_RESULT(no) ;; *freebsd*) - # FreeBSD's implementation of thread_local is also buggy (per - # https://groups.google.com/d/msg/bsdmailinglist/22ncTZAbDp4/Dii_pII5AwAJ) + dnl FreeBSD's implementation of thread_local is also buggy (per + dnl https://groups.google.com/d/msg/bsdmailinglist/22ncTZAbDp4/Dii_pII5AwAJ) AC_MSG_RESULT(no) ;; *) @@ -1068,7 +1068,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], ] ) -# Check for different ways of gathering OS randomness +dnl Check for different ways of gathering OS randomness AC_MSG_CHECKING(for Linux getrandom syscall) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include #include @@ -1124,7 +1124,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include [ AC_MSG_RESULT(no)] ) -# ensure backtrace() is found, check -lexecinfo if necessary +dnl ensure backtrace() is found, check -lexecinfo if necessary if test x$TARGET_OS != xwindows; then if test "x$enable_stacktraces" != xno; then AC_SEARCH_LIBS([backtrace], [execinfo], [], [ diff --git a/contrib/devtools/README.md b/contrib/devtools/README.md index 245f3af3a79e..7a9815525200 100644 --- a/contrib/devtools/README.md +++ b/contrib/devtools/README.md @@ -159,7 +159,7 @@ repository (requires pngcrush). security-check.py and test-security-check.py ============================================ -Perform basic ELF security checks on a series of executables. +Perform basic security checks on a series of executables. symbol-check.py =============== diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index f73247e50e5c..2eeca5111746 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -3,10 +3,10 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' -Perform basic ELF security checks on a series of executables. +Perform basic security checks on a series of executables. Exit status will be 0 if successful, and the program will be silent. Otherwise the exit status will be 1 and it will log which executables failed which checks. -Needs `readelf` (for ELF) and `objdump` (for PE). +Needs `readelf` (for ELF), `objdump` (for PE) and `otool` (for MACHO). ''' import subprocess import sys @@ -14,6 +14,7 @@ READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') OBJDUMP_CMD = os.getenv('OBJDUMP', '/usr/bin/objdump') +OTOOL_CMD = os.getenv('OTOOL', '/usr/bin/otool') NONFATAL = {} # checks which are non-fatal for now but only generate a warning def check_ELF_PIE(executable): @@ -162,6 +163,40 @@ def check_PE_NX(executable): (arch,bits) = get_PE_dll_characteristics(executable) return (bits & IMAGE_DLL_CHARACTERISTICS_NX_COMPAT) == IMAGE_DLL_CHARACTERISTICS_NX_COMPAT +def get_MACHO_executable_flags(executable): + p = subprocess.Popen([OTOOL_CMD, '-vh', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) + (stdout, stderr) = p.communicate() + if p.returncode: + raise IOError('Error opening file') + + flags = [] + for line in stdout.splitlines(): + tokens = line.split() + # filter first two header lines + if 'magic' in tokens or 'Mach' in tokens: + continue + # filter ncmds and sizeofcmds values + flags += [t for t in tokens if not t.isdigit()] + return flags + +def check_MACHO_PIE(executable) -> bool: + ''' + Check for position independent executable (PIE), allowing for address space randomization. + ''' + flags = get_MACHO_executable_flags(executable) + if 'PIE' in flags: + return True + return False + +def check_MACHO_NOUNDEFS(executable) -> bool: + ''' + Check for no undefined references. + ''' + flags = get_MACHO_executable_flags(executable) + if 'NOUNDEFS' in flags: + return True + return False + CHECKS = { 'ELF': [ ('PIE', check_ELF_PIE), @@ -173,6 +208,10 @@ def check_PE_NX(executable): ('DYNAMIC_BASE', check_PE_DYNAMIC_BASE), ('HIGH_ENTROPY_VA', check_PE_HIGH_ENTROPY_VA), ('NX', check_PE_NX) +], +'MACHO': [ + ('PIE', check_MACHO_PIE), + ('NOUNDEFS', check_MACHO_NOUNDEFS), ] } @@ -183,6 +222,8 @@ def identify_executable(executable): return 'PE' elif magic.startswith(b'\x7fELF'): return 'ELF' + elif magic.startswith(b'\xcf\xfa'): + return 'MACHO' return None if __name__ == '__main__': diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 4a70cbc760f8..2dc7627602dd 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -145,6 +145,7 @@ script: | CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} make ${MAKEOPTS} make -C src osx_debug + make ${MAKEOPTS} -C src check-security make install-strip DESTDIR=${INSTALLPATH} make osx_volname diff --git a/depends/packages/xcb_proto.mk b/depends/packages/xcb_proto.mk index 44110394bdda..01203a0718af 100644 --- a/depends/packages/xcb_proto.mk +++ b/depends/packages/xcb_proto.mk @@ -4,11 +4,6 @@ $(package)_download_path=https://xcb.freedesktop.org/dist $(package)_file_name=xcb-proto-$($(package)_version).tar.bz2 $(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05 -define $(package)_set_vars - $(package)_config_opts=--disable-shared - $(package)_config_opts_linux=--with-pic -endef - define $(package)_config_cmds $($(package)_autoconf) endef diff --git a/doc/developer-notes.md b/doc/developer-notes.md index e6861d51d53d..28cf00dbd52f 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -1126,7 +1126,7 @@ A few guidelines for introducing and reviewing new RPC interfaces: - A RPC method must either be a wallet method or a non-wallet method. Do not introduce new methods that differ in behavior based on the presence of a wallet. - - *Rationale*: as well as complicating the implementation and interfering + - *Rationale*: As well as complicating the implementation and interfering with the introduction of multi-wallet, wallet and non-wallet code should be separated to avoid introducing circular dependencies between code units. @@ -1153,8 +1153,13 @@ A few guidelines for introducing and reviewing new RPC interfaces: - *Rationale*: RPC methods registered with the same function pointer will be considered aliases and only the first method name will show up in the - `help` rpc command list. + `help` RPC command list. - *Exception*: Using RPC method aliases may be appropriate in cases where a new RPC is replacing a deprecated RPC, to avoid both RPCs confusingly showing up in the command list. + +- Use the `UNIX_EPOCH_TIME` constant when describing UNIX epoch time or + timestamps in the documentation. + + - *Rationale*: User-facing consistency. diff --git a/release-notes-17743.md b/release-notes-17743.md new file mode 100644 index 000000000000..730c5419ac7d --- /dev/null +++ b/release-notes-17743.md @@ -0,0 +1,5 @@ +New settings +------------ + +- RPC Whitelist system. It can give certain RPC users permissions to only some RPC calls. +It can be set with two command line arguments (`rpcwhitelist` and `rpcwhitelistdefault`). (#12763) diff --git a/src/Makefile.am b/src/Makefile.am index 4ce5d65836a0..8101638eec72 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -911,7 +911,7 @@ endif check-security: $(bin_PROGRAMS) if HARDEN @echo "Checking binary security..." - $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) + $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) endif diff --git a/src/base58.cpp b/src/base58.cpp index 849293634a5a..4be95be1737e 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -12,6 +12,8 @@ #include #include +#include + /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static const int8_t mapBase58[256] = { @@ -33,7 +35,7 @@ static const int8_t mapBase58[256] = { -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, }; -bool DecodeBase58(const char* psz, std::vector& vch) +bool DecodeBase58(const char* psz, std::vector& vch, int max_ret_len) { // Skip leading spaces. while (*psz && IsSpace(*psz)) @@ -43,6 +45,7 @@ bool DecodeBase58(const char* psz, std::vector& vch) int length = 0; while (*psz == '1') { zeroes++; + if (zeroes > max_ret_len) return false; psz++; } // Allocate enough space in big-endian base256 representation. @@ -63,6 +66,7 @@ bool DecodeBase58(const char* psz, std::vector& vch) } assert(carry == 0); length = i; + if (length + zeroes > max_ret_len) return false; psz++; } // Skip trailing spaces. @@ -72,8 +76,6 @@ bool DecodeBase58(const char* psz, std::vector& vch) return false; // Skip leading zeroes in b256. std::vector::iterator it = b256.begin() + (size - length); - while (it != b256.end() && *it == 0) - it++; // Copy result into output vector. vch.reserve(zeroes + (b256.end() - it)); vch.assign(zeroes, 0x00); @@ -127,12 +129,12 @@ std::string EncodeBase58(const std::vector& vch) return EncodeBase58(vch.data(), vch.data() + vch.size()); } -bool DecodeBase58(const std::string& str, std::vector& vchRet) +bool DecodeBase58(const std::string& str, std::vector& vchRet, int max_ret_len) { if (!ValidAsCString(str)) { return false; } - return DecodeBase58(str.c_str(), vchRet); + return DecodeBase58(str.c_str(), vchRet, max_ret_len); } std::string EncodeBase58Check(const std::vector& vchIn) @@ -144,9 +146,9 @@ std::string EncodeBase58Check(const std::vector& vchIn) return EncodeBase58(vch); } -bool DecodeBase58Check(const char* psz, std::vector& vchRet) +bool DecodeBase58Check(const char* psz, std::vector& vchRet, int max_ret_len) { - if (!DecodeBase58(psz, vchRet) || + if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits::max() - 4 ? std::numeric_limits::max() : max_ret_len + 4) || (vchRet.size() < 4)) { vchRet.clear(); return false; @@ -161,11 +163,11 @@ bool DecodeBase58Check(const char* psz, std::vector& vchRet) return true; } -bool DecodeBase58Check(const std::string& str, std::vector& vchRet) +bool DecodeBase58Check(const std::string& str, std::vector& vchRet, int max_ret) { if (!ValidAsCString(str)) { return false; } - return DecodeBase58Check(str.c_str(), vchRet); + return DecodeBase58Check(str.c_str(), vchRet, max_ret); } diff --git a/src/base58.h b/src/base58.h index 038e3288cd3f..2049f105737a 100644 --- a/src/base58.h +++ b/src/base58.h @@ -35,13 +35,13 @@ std::string EncodeBase58(const std::vector& vch); * return true if decoding is successful. * psz cannot be nullptr. */ -[[nodiscard]] bool DecodeBase58(const char* psz, std::vector& vchRet); +[[nodiscard]] bool DecodeBase58(const char* psz, std::vector& vchRet, int max_ret_len); /** * Decode a base58-encoded string (str) into a byte vector (vchRet). * return true if decoding is successful. */ -[[nodiscard]] bool DecodeBase58(const std::string& str, std::vector& vchRet); +[[nodiscard]] bool DecodeBase58(const std::string& str, std::vector& vchRet, int max_ret_len); /** * Encode a byte vector into a base58-encoded string, including checksum @@ -52,12 +52,12 @@ std::string EncodeBase58Check(const std::vector& vchIn); * Decode a base58-encoded string (psz) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -[[nodiscard]] bool DecodeBase58Check(const char* psz, std::vector& vchRet); +[[nodiscard]] bool DecodeBase58Check(const char* psz, std::vector& vchRet, int max_ret_len); /** * Decode a base58-encoded string (str) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -[[nodiscard]] bool DecodeBase58Check(const std::string& str, std::vector& vchRet); +[[nodiscard]] bool DecodeBase58Check(const std::string& str, std::vector& vchRet, int max_ret_len); #endif // BITCOIN_BASE58_H diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp index 6d283cfd7a82..5e7428febb5f 100644 --- a/src/bench/base58.cpp +++ b/src/bench/base58.cpp @@ -47,7 +47,7 @@ static void Base58Decode(benchmark::Bench& bench) const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"; std::vector vch; bench.batch(strlen(addr)).unit("byte").run([&] { - (void) DecodeBase58(addr, vch); + (void) DecodeBase58(addr, vch, 64); }); } diff --git a/src/httprpc.cpp b/src/httprpc.cpp index aa8ae8581f31..bb975a4ea11f 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -15,7 +15,12 @@ #include #include +#include +#include +#include #include +#include +#include #include // boost::trim @@ -63,6 +68,9 @@ class HTTPRPCTimerInterface : public RPCTimerInterface static std::string strRPCUserColonPass; /* Stored RPC timer interface (for unregistration) */ static std::unique_ptr httpRPCTimerInterface; +/* RPC Auth Whitelist */ +static std::map> g_rpc_whitelist; +static bool g_rpc_whitelist_default = false; static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id) { @@ -185,18 +193,45 @@ static bool HTTPReq_JSONRPC(const util::Ref& context, HTTPRequest* req) jreq.URI = req->GetURI(); std::string strReply; + bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser); + if (!user_has_whitelist && g_rpc_whitelist_default) { + LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser); + req->WriteReply(HTTP_FORBIDDEN); + return false; + // singleton request - if (valRequest.isObject()) { + } else if (valRequest.isObject()) { jreq.parse(valRequest); - + if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) { + LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod); + req->WriteReply(HTTP_FORBIDDEN); + return false; + } UniValue result = tableRPC.execute(jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests - } else if (valRequest.isArray()) + } else if (valRequest.isArray()) { + if (user_has_whitelist) { + for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) { + if (!valRequest[reqIdx].isObject()) { + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + } else { + const UniValue& request = valRequest[reqIdx].get_obj(); + // Parse method + std::string strMethod = find_value(request, "method").get_str(); + if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) { + LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod); + req->WriteReply(HTTP_FORBIDDEN); + return false; + } + } + } + } strReply = JSONRPCExecBatch(jreq, valRequest.get_array()); + } else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); @@ -231,6 +266,27 @@ static bool InitRPCAuthentication() { LogPrintf("Using rpcauth authentication.\n"); } + + g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist")); + for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) { + auto pos = strRPCWhitelist.find(':'); + std::string strUser = strRPCWhitelist.substr(0, pos); + bool intersect = g_rpc_whitelist.count(strUser); + std::set& whitelist = g_rpc_whitelist[strUser]; + if (pos != std::string::npos) { + std::string strWhitelist = strRPCWhitelist.substr(pos + 1); + std::set new_whitelist; + boost::split(new_whitelist, strWhitelist, boost::is_any_of(", ")); + if (intersect) { + std::set tmp_whitelist; + std::set_intersection(new_whitelist.begin(), new_whitelist.end(), + whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end())); + new_whitelist = std::move(tmp_whitelist); + } + whitelist = std::move(new_whitelist); + } + } + return true; } diff --git a/src/init.cpp b/src/init.cpp index a43e4c356ba7..12c94225e5dc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -712,7 +712,7 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-maxsigcachesize=", strprintf("Limit sum of signature cache and script execution cache sizes to MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-maxtipage=", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); - argsman.AddArg("-mocktime=", "Replace actual time with seconds since epoch (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-mocktime=", "Replace actual time with " + UNIX_EPOCH_TIME + "(default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-minsporkkeys=", "Overrides minimum spork signers to change spork value. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); @@ -754,6 +754,8 @@ void SetupServerArgs(NodeContext& node) argsman.AddArg("-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcthreads=", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcwhitelist=", "Set a whitelist to filter incoming RPC calls for a specific user. The field comes in the format: :,,...,. If multiple whitelists are set for a given user, they are set-intersected. See -rpcwhitelistdefault documentation for information on default whitelist behavior.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_BOOL, OptionsCategory::RPC); argsman.AddArg("-rpcworkqueue=", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); diff --git a/src/key_io.cpp b/src/key_io.cpp index 16e89b43155b..f02bbb510f6e 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -45,7 +45,7 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par { std::vector data; uint160 hash; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 21)) { // base58-encoded Dash addresses. // Public-key-hash-addresses have version 76 (or 140 testnet). // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. @@ -70,7 +70,7 @@ CKey DecodeSecret(const std::string& str) { CKey key; std::vector data; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 34)) { const std::vector& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY); if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { @@ -101,7 +101,7 @@ CExtPubKey DecodeExtPubKey(const std::string& str) { CExtPubKey key; std::vector data; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 78)) { const std::vector& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { key.Decode(data.data() + prefix.size()); @@ -124,7 +124,7 @@ CExtKey DecodeExtKey(const std::string& str) { CExtKey key; std::vector data; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 78)) { const std::vector& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { key.Decode(data.data() + prefix.size()); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 743bfa1f6523..3fdada4cd0d0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -448,7 +448,7 @@ static std::vector MempoolEntryDescription() { return { "size will be completely removed in v0.20."}, RPCResult{RPCResult::Type::STR_AMOUNT, "fee", "transaction fee in " + CURRENCY_UNIT + " (DEPRECATED)"}, RPCResult{RPCResult::Type::STR_AMOUNT, "modifiedfee", "transaction fee with fee deltas used for mining priority (DEPRECATED)"}, - RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in seconds since 1 Jan 1970 GMT"}, + RPCResult{RPCResult::Type::NUM_TIME, "time", "local time transaction entered pool in " + UNIX_EPOCH_TIME}, RPCResult{RPCResult::Type::NUM, "height", "block height when transaction entered pool"}, RPCResult{RPCResult::Type::NUM, "descendantcount", "number of in-mempool descendant transactions (including this one)"}, RPCResult{RPCResult::Type::NUM, "descendantsize", "size of in-mempool descendants (including this one)"}, @@ -812,8 +812,8 @@ static UniValue getblockheader(const JSONRPCRequest& request) {RPCResult::Type::NUM, "version", "The block version"}, {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, - {RPCResult::Type::NUM_TIME, "time", "The block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, - {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "nonce", "The nonce"}, {RPCResult::Type::STR_HEX, "bits", "The bits"}, {RPCResult::Type::NUM, "difficulty", "The difficulty"}, @@ -883,8 +883,8 @@ static UniValue getblockheaders(const JSONRPCRequest& request) {RPCResult::Type::NUM, "version", "The block version"}, {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, - {RPCResult::Type::NUM_TIME, "time", "The block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, - {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "nonce", "The nonce"}, {RPCResult::Type::STR_HEX, "bits", "The bits"}, {RPCResult::Type::NUM, "difficulty", "The difficulty"}, @@ -1090,8 +1090,8 @@ static UniValue getblock(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "merkleRootMNList", "The merkle root of the masternode list"}, {RPCResult::Type::STR_HEX, "merkleRootQuorums", "The merkle root of the quorum list"}, }}, - {RPCResult::Type::NUM_TIME, "time", "The block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, - {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, + {RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME}, + {RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "nonce", "The nonce"}, {RPCResult::Type::STR_HEX, "bits", "The bits"}, {RPCResult::Type::NUM, "difficulty", "The difficulty"}, @@ -1155,7 +1155,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) { RPCHelpMan{"pruneblockchain", "", { - {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n" + {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n" " to prune blocks whose block time is at least 2 hours older than the provided timestamp."}, }, RPCResult{ @@ -1874,7 +1874,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) RPCResult{ RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window in UNIX format."}, + {RPCResult::Type::NUM_TIME, "time", "The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "txcount", "The total number of transactions in the chain up to that point"}, {RPCResult::Type::STR_HEX, "window_final_block_hash", "The hash of the final block in the window"}, {RPCResult::Type::NUM, "window_final_block_height", "The height of the final block in the window."}, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index a6cfb64bcdd4..38a2e2bcd5a4 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -576,7 +576,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) {RPCResult::Type::ELISION, "", ""}, }}, {RPCResult::Type::STR, "target", "The hash target"}, - {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in seconds since epoch (Jan 1 1970 GMT)"}, + {RPCResult::Type::NUM_TIME, "mintime", "The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::ARR, "mutable", "list of ways the block template may be changed", { {RPCResult::Type::STR, "value", "A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'"}, @@ -584,7 +584,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "noncerange", "A range of valid nonces"}, {RPCResult::Type::NUM, "sigoplimit", "limit of sigops in blocks"}, {RPCResult::Type::NUM, "sizelimit", "limit of block size"}, - {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in seconds since epoch (Jan 1 1970 GMT)"}, + {RPCResult::Type::NUM_TIME, "curtime", "current timestamp in " + UNIX_EPOCH_TIME}, {RPCResult::Type::STR, "bits", "compressed target of next block"}, {RPCResult::Type::STR, "previousbits", "compressed target of current highest block"}, {RPCResult::Type::NUM, "height", "The height of the next block"}, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index aaa87146e656..a39415c78185 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -511,7 +511,7 @@ static UniValue setmocktime(const JSONRPCRequest& request) RPCHelpMan{"setmocktime", "\nSet the local time to given timestamp (-regtest only)\n", { - {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Unix seconds-since-epoch timestamp\n" + {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n" " Pass 0 to go back to using the system time."}, }, RPCResult{RPCResult::Type::NONE, "", ""}, diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 25cea2570ff6..2bdaa6de23e0 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -102,11 +102,11 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"} }}, {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"}, - {RPCResult::Type::NUM_TIME, "lastsend", "The time in seconds since epoch (Jan 1 1970 GMT) of the last send"}, - {RPCResult::Type::NUM_TIME, "lastrecv", "The time in seconds since epoch (Jan 1 1970 GMT) of the last receive"}, + {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"}, + {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"}, {RPCResult::Type::NUM, "bytessent", "The total bytes sent"}, {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"}, - {RPCResult::Type::NUM_TIME, "conntime", "The time in seconds since epoch (Jan 1 1970 GMT) of the connection"}, + {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"}, {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"}, {RPCResult::Type::NUM, "pingtime", "ping time (if available)"}, {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"}, @@ -588,7 +588,7 @@ static UniValue setban(const JSONRPCRequest& request) {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"}, {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"}, {"bantime", RPCArg::Type::NUM, /* default */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"}, - {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"}, + {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp expressed in " + UNIX_EPOCH_TIME}, }, RPCResult{RPCResult::Type::NONE, "", ""}, RPCExamples{ @@ -760,7 +760,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) { {RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::NUM_TIME, "time", "Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen"}, + {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"}, {RPCResult::Type::NUM, "services", "The services offered"}, {RPCResult::Type::STR, "address", "The address of the node"}, {RPCResult::Type::NUM, "port", "The port of the node"}, diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 47579fa80a04..e83b228caaba 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -183,7 +183,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "blockhash", "the block hash"}, {RPCResult::Type::NUM, "height", "The block height"}, {RPCResult::Type::NUM, "confirmations", "The confirmations"}, - {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in seconds since epoch (Jan 1 1970 GMT)"}, + {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME}, {RPCResult::Type::NUM, "time", "Same as \"blocktime\""}, {RPCResult::Type::BOOL, "instantlock", "Current transaction lock state"}, {RPCResult::Type::BOOL, "instantlock_internal", "Current internal transaction lock state"}, diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index 17b3e4f44417..5ec08a88428e 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -17,6 +17,9 @@ #include #include +const std::string UNIX_EPOCH_TIME = "UNIX epoch time"; + + void RPCTypeCheck(const UniValue& params, const std::list& typesExpected, bool fAllowNull) diff --git a/src/rpc/util.h b/src/rpc/util.h index f6ed1711bda9..b1975707c6be 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -21,7 +21,14 @@ #include +/** + * String used to describe UNIX epoch time in documentation, factored out to a + * constant for consistency. + */ +extern const std::string UNIX_EPOCH_TIME; + class CKeyStore; +class FillableSigningProvider; class CPubKey; class CScript; struct Sections; diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 679de3e9b18d..ab9e78515646 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -53,29 +54,45 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) } std::vector expected = ParseHex(test[0].get_str()); std::string base58string = test[1].get_str(); - BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest); + BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result, 256), strTest); BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest); } - BOOST_CHECK(!DecodeBase58("invalid", result)); - BOOST_CHECK(!DecodeBase58(std::string("invalid"), result)); - BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result)); + BOOST_CHECK(!DecodeBase58("invalid", result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("invalid"), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result, 100)); - BOOST_CHECK(DecodeBase58(std::string("good", 4), result)); - BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result)); - BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result)); - BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result)); + BOOST_CHECK(DecodeBase58(std::string("good", 4), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result, 100)); // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end. - BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result)); - BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result)); + BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3)); + BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3)); std::vector expected = ParseHex("971a55"); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); - BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result)); - BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result)); - BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result)); - BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result)); + BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result, 100)); + BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result, 100)); + BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result, 100)); + BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result, 100)); +} + +BOOST_AUTO_TEST_CASE(base58_random_encode_decode) +{ + for (int n = 0; n < 1000; ++n) { + unsigned int len = 1 + InsecureRandBits(8); + unsigned int zeroes = InsecureRandBool() ? InsecureRandRange(len + 1) : 0; + auto data = Cat(std::vector(zeroes, '\000'), g_insecure_rand_ctx.randbytes(len - zeroes)); + auto encoded = EncodeBase58Check(data); + std::vector decoded; + auto ok_too_small = DecodeBase58Check(encoded, decoded, InsecureRandRange(len)); + BOOST_CHECK(!ok_too_small); + auto ok = DecodeBase58Check(encoded, decoded, len + InsecureRandRange(257 - len)); + BOOST_CHECK(ok); + BOOST_CHECK(data == decoded); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/asmap_direct.cpp b/src/test/fuzz/asmap_direct.cpp index 790f80237d1f..859f40c15f81 100644 --- a/src/test/fuzz/asmap_direct.cpp +++ b/src/test/fuzz/asmap_direct.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include diff --git a/src/test/fuzz/base_encode_decode.cpp b/src/test/fuzz/base_encode_decode.cpp index 02bac68c9364..555865c1b6c1 100644 --- a/src/test/fuzz/base_encode_decode.cpp +++ b/src/test/fuzz/base_encode_decode.cpp @@ -18,13 +18,13 @@ void test_one_input(const std::vector& buffer) const std::string random_encoded_string(buffer.begin(), buffer.end()); std::vector decoded; - if (DecodeBase58(random_encoded_string, decoded)) { + if (DecodeBase58(random_encoded_string, decoded, buffer.size())) { const std::string encoded_string = EncodeBase58(decoded); assert(encoded_string == TrimString(encoded_string)); assert(ToLower(encoded_string) == ToLower(TrimString(random_encoded_string))); } - if (DecodeBase58Check(random_encoded_string, decoded)) { + if (DecodeBase58Check(random_encoded_string, decoded, buffer.size())) { const std::string encoded_string = EncodeBase58Check(decoded); assert(encoded_string == TrimString(encoded_string)); assert(ToLower(encoded_string) == ToLower(TrimString(random_encoded_string))); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index c7806df6473d..57774b7cd09b 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1430,7 +1430,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor", /* oneline_description */ "", {"\"