Skip to content

Commit

Permalink
Support nanosecond precision for dates
Browse files Browse the repository at this point in the history
Along with several other changes:

- LC_ALL is set to 'C', so 'date' is executed in the C locale.
- A more useful error message is given if the received date is not in
  UTC.
- Dates too far in the past (sufficiently before these changes were
  made) are rejected, as they cannot possibly be valid.
- A trailing newline is required.
- Invalid dates that are missing a ':' in the timezone are rejected.
- A more useful error message is given if the received date is not
  ASCII.  Previously this crashed the program with a UnicodeDecodeError.
  • Loading branch information
DemiMarie committed Jul 10, 2022
1 parent 3508eda commit accfebc
Showing 1 changed file with 31 additions and 8 deletions.
39 changes: 31 additions & 8 deletions qvm-tools/qvm-sync-clock
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import subprocess
from qubesadmin import Qubes

def main():
os.environ['LC_ALL'] = 'C'
if os.geteuid() != 0:
sys.stderr.write('This program must be run as root to set the date, aborting!\n')
sys.exit(1)
Expand All @@ -36,21 +37,43 @@ def main():
sys.exit(0)

if not clockvm.is_running():
sys.stderr.write('ClockVM {} is not running, aborting.\n'.format(
sys.stderr.write('ClockVM {} is not running, aborting!\n'.format(
clockvm.name))
sys.exit(0)

p = clockvm.run_service('qubes.GetDate')
untrusted_date_out = p.stdout.read(25).decode('ascii', errors='strict')
untrusted_date_out = untrusted_date_out.strip()

if not re.match(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+00:?00$', untrusted_date_out):
p = clockvm.run_service('qubes.GetDate+nanoseconds')
untrusted_date_out = p.stdout.read(36)
try:
untrusted_date_out.decode('ascii', 'strict')
except UnicodeDecodeError:
sys.stderr.write('Received non-ASCII date, aborting!\n')
sys.exit(1)
untrusted_date_len = len(untrusted_date_out)
if untrusted_date_len == 36: # new format, nanosecond precision
regexp = rb'\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2},[0-9]{9}\+00:00\n\Z'
precision = b'ns'
elif untrusted_date_len == 26: # old format, second precision
regexp = rb'\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+00:00\n\Z'
precision = b'seconds'
else:
sys.stderr.write('Invalid date length (expected 26 or 36 bytes, got {})'
', aborting!\n'.format(untrusted_date_len))
sys.exit(1)
if untrusted_date_out[-7:] != b'+00:00\n':
sys.stderr.write('Date not in UTC, aborting!\n')
sys.exit(1)
if not re.match(regexp, untrusted_date_out):
sys.stderr.write('Invalid date received, aborting!\n')
sys.exit(1)
# this time is arbitrary, something better should be used instead
if untrusted_date_out[:19] <= b'2022-07-10T17:08:31':
sys.stderr.write('Received a date older than this program, aborting!\n')
sys.exit(1)
date_out = untrusted_date_out
subprocess.check_call(['date', '-u', '-Iseconds', '-s', date_out],
subprocess.check_call([b'date', b'-u', b'-I' + precision, b'-s',
date_out[:-1]],
stdout=subprocess.DEVNULL)
subprocess.check_call(['/sbin/hwclock', '--systohc'],
subprocess.check_call([b'/sbin/hwclock', b'--systohc'],
stdout=subprocess.DEVNULL)

if __name__ == '__main__':
Expand Down

0 comments on commit accfebc

Please sign in to comment.