give clean error msg for invalid nonce file, see #7967

That rather long traceback was not pretty.

Of course users should usually have valid nonce files,
but multiple users managed to corrupt the file contents somehow.

Also:
- add Error / ErrorWithTraceback exception classes to RPC layer.
- add hex_to_bin helper
- also call hex_to_bin for the local nonce file (security dir)
This commit is contained in:
Thomas Waldmann 2024-01-16 23:24:25 +01:00
parent 5b77bfef06
commit d9132a34d4
No known key found for this signature in database
GPG Key ID: 243ACFA951F78E01
4 changed files with 36 additions and 9 deletions

View File

@ -1,9 +1,9 @@
import os
import sys
from binascii import unhexlify
from ..helpers import Error
from ..helpers import get_security_dir
from ..helpers import bin_to_hex
from ..helpers import bin_to_hex, hex_to_bin
from ..platform import SaveFile
from ..remote import InvalidRPCMethod
@ -23,9 +23,15 @@ class NonceManager:
def get_local_free_nonce(self):
try:
with open(self.nonce_file) as fd:
return bytes_to_long(unhexlify(fd.read()))
nonce_hex = fd.read().strip()
except FileNotFoundError:
return None
else:
try:
nonce_bytes = hex_to_bin(nonce_hex, length=8)
except ValueError as e:
raise Error(f"Local security dir has an invalid nonce file: {e}") from None
return bytes_to_long(nonce_bytes)
def commit_local_nonce_reservation(self, next_unreserved, start_nonce):
if self.get_local_free_nonce() != start_nonce:

View File

@ -1,4 +1,5 @@
import argparse
import binascii
import hashlib
import json
import os
@ -8,7 +9,6 @@ import shlex
import socket
import stat
import uuid
from binascii import hexlify
from collections import Counter, OrderedDict
from datetime import datetime, timezone
from functools import partial
@ -27,7 +27,18 @@ from ..platformflags import is_win32
def bin_to_hex(binary):
return hexlify(binary).decode('ascii')
return binascii.hexlify(binary).decode('ascii')
def hex_to_bin(hex, length=None):
try:
binary = binascii.unhexlify(hex)
binary_len = len(binary)
if length is not None and binary_len != length:
raise ValueError(f"Expected {length} bytes ({2 * length} hex digits), got {binary_len} bytes.")
except binascii.Error as e:
raise ValueError(str(e)) from None
return binary
def safe_decode(s, coding='utf-8', errors='surrogateescape'):

View File

@ -18,7 +18,7 @@ from subprocess import Popen, PIPE
from . import __version__
from .compress import Compressor
from .constants import * # NOQA
from .helpers import Error, IntegrityError
from .helpers import Error, ErrorWithTraceback, IntegrityError
from .helpers import bin_to_hex
from .helpers import get_base_dir
from .helpers import get_limited_unpacker
@ -747,7 +747,11 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+.
old_server = b'exception_args' not in unpacked
args = unpacked.get(b'exception_args')
if error == 'DoesNotExist':
if error == 'Error':
raise Error(args[0].decode())
elif error == 'ErrorWithTraceback':
raise ErrorWithTraceback(args[0].decode())
elif error == 'DoesNotExist':
raise Repository.DoesNotExist(self.location.processed)
elif error == 'AlreadyExists':
raise Repository.AlreadyExists(self.location.processed)

View File

@ -16,7 +16,7 @@ from .hashindex import NSIndex
from .helpers import Error, ErrorWithTraceback, IntegrityError, format_file_size, parse_file_size
from .helpers import Location
from .helpers import ProgressIndicatorPercent
from .helpers import bin_to_hex
from .helpers import bin_to_hex, hex_to_bin
from .helpers import secure_erase, safe_unlink
from .helpers import Manifest
from .helpers import msgpack
@ -363,9 +363,15 @@ class Repository:
nonce_path = os.path.join(self.path, 'nonce')
try:
with open(nonce_path) as fd:
return int.from_bytes(unhexlify(fd.read()), byteorder='big')
nonce_hex = fd.read().strip()
except FileNotFoundError:
return None
else:
try:
nonce_bytes = hex_to_bin(nonce_hex, length=8)
except ValueError as e:
raise Error(f"Repository has an invalid nonce file: {e}") from None
return int.from_bytes(nonce_bytes, byteorder='big')
def commit_nonce_reservation(self, next_unreserved, start_nonce):
if self.do_lock and not self.lock.got_exclusive_lock():