fix: NTP syncs as soon as DHCP has a lease, random origin cookie instead of the unsynced local clock, TLS validates certificates against UTC
This commit is contained in:
1 parent
1d00c1c016
commit
7e036c94e7
13 files changed
+51
-59
No files matched your search
@@ -12,4 +12,4 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define MONTAUK_BUILD_NUMBER 103
|
#define MONTAUK_BUILD_NUMBER 107
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
#include "Terminal.hpp" // SYS_PRINT, SYS_PUTCHAR
|
#include "Terminal.hpp" // SYS_PRINT, SYS_PUTCHAR
|
||||||
#include "Filesystem.hpp" // SYS_OPEN, SYS_READ, SYS_GETSIZE, SYS_CLOSE, SYS_READDIR, SYS_FWRITE, SYS_FCREATE
|
#include "Filesystem.hpp" // SYS_OPEN, SYS_READ, SYS_GETSIZE, SYS_CLOSE, SYS_READDIR, SYS_FWRITE, SYS_FCREATE
|
||||||
#include "Heap.hpp" // SYS_ALLOC, SYS_FREE
|
#include "Heap.hpp" // SYS_ALLOC, SYS_FREE
|
||||||
#include "Time.hpp" // SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME
|
#include "Time.hpp" // SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME, SYS_GETUNIXTIME
|
||||||
#include "Keyboard.hpp" // SYS_ISKEYAVAILABLE, SYS_GETKEY, SYS_GETCHAR
|
#include "Keyboard.hpp" // SYS_ISKEYAVAILABLE, SYS_GETKEY, SYS_GETCHAR
|
||||||
#include "Info.hpp" // SYS_GETINFO
|
#include "Info.hpp" // SYS_GETINFO
|
||||||
#include "Graphics.hpp" // SYS_FBINFO, SYS_FBMAP, SYS_TERMSIZE, SYS_TERMSCALE
|
#include "Graphics.hpp" // SYS_FBINFO, SYS_FBMAP, SYS_TERMSIZE, SYS_TERMSCALE
|
||||||
@@ -245,6 +245,8 @@ namespace montauk::abi {
|
|||||||
if (!UserMemory::Writable<DateTime>(frame->arg1)) return BadPointer;
|
if (!UserMemory::Writable<DateTime>(frame->arg1)) return BadPointer;
|
||||||
Sys_GetTime((DateTime*)frame->arg1);
|
Sys_GetTime((DateTime*)frame->arg1);
|
||||||
return Success;
|
return Success;
|
||||||
|
case SYS_GETUNIXTIME:
|
||||||
|
return Sys_GetUnixTime();
|
||||||
case SYS_SETUNIXTIME:
|
case SYS_SETUNIXTIME:
|
||||||
if (!Sched::Consent::Require(CAP_SET_TIME)) return CapabilityDenied;
|
if (!Sched::Consent::Require(CAP_SET_TIME)) return CapabilityDenied;
|
||||||
return Sys_SetUnixTime((int64_t)frame->arg1);
|
return Sys_SetUnixTime((int64_t)frame->arg1);
|
||||||
|
|||||||
@@ -405,6 +405,9 @@ namespace montauk::abi {
|
|||||||
and returns 1 while more remains, 0 once done. CAP_STORAGE_ADMIN. */
|
and returns 1 while more remains, 0 once done. CAP_STORAGE_ADMIN. */
|
||||||
static constexpr uint64_t SYS_FS_GROW = 208; // (drive, FsGrowStatus*, apply) -> 1 / 0 / status
|
static constexpr uint64_t SYS_FS_GROW = 208; // (drive, FsGrowStatus*, apply) -> 1 / 0 / status
|
||||||
|
|
||||||
|
/* Time.hpp -- wall clock as UTC unix seconds (SYS_GETTIME is local time). */
|
||||||
|
static constexpr uint64_t SYS_GETUNIXTIME = 209; // () -> seconds
|
||||||
|
|
||||||
/* Kernel-owned process capabilities. User identities may namespace
|
/* Kernel-owned process capabilities. User identities may namespace
|
||||||
per-user resources, but never participate in authorization decisions. */
|
per-user resources, but never participate in authorization decisions. */
|
||||||
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Time.hpp
|
* Time.hpp
|
||||||
* SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME syscalls
|
* SYS_GETTICKS, SYS_GETMILLISECONDS, SYS_GETTIME, SYS_GETUNIXTIME syscalls
|
||||||
* Copyright (c) 2026 Daniel Hammer
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -39,6 +39,10 @@ namespace montauk::abi {
|
|||||||
return (int64_t)Timekeeping::GetTZOffset();
|
return (int64_t)Timekeeping::GetTZOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int64_t Sys_GetUnixTime() {
|
||||||
|
return Timekeeping::GetUnixTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
static int64_t Sys_SetUnixTime(int64_t unixSeconds) {
|
static int64_t Sys_SetUnixTime(int64_t unixSeconds) {
|
||||||
return Timekeeping::SetUnixTimestamp(unixSeconds) ? Success : InvalidArgument;
|
return Timekeeping::SetUnixTimestamp(unixSeconds) ? Success : InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ static int64_t DateToEpoch(int year, int month, int day, int hour, int minute, i
|
|||||||
|
|
||||||
static Timekeeping::DateTime EpochToDate(int64_t epoch) {
|
static Timekeeping::DateTime EpochToDate(int64_t epoch) {
|
||||||
Timekeeping::DateTime dt = {};
|
Timekeeping::DateTime dt = {};
|
||||||
int64_t rem = epoch;
|
/* Before the first NTP sync on a board with no RTC the clock sits near
|
||||||
|
the epoch, and a negative time zone pushes local time before it. */
|
||||||
|
int64_t rem = epoch < 0 ? 0 : epoch;
|
||||||
int year = 1970;
|
int year = 1970;
|
||||||
while (true) {
|
while (true) {
|
||||||
int64_t daysInYear = IsLeapYear(year) ? 366 : 365;
|
int64_t daysInYear = IsLeapYear(year) ? 366 : 365;
|
||||||
|
|||||||
@@ -317,6 +317,9 @@ namespace montauk::abi {
|
|||||||
and returns 1 while more remains, 0 once done. CAP_STORAGE_ADMIN. */
|
and returns 1 while more remains, 0 once done. CAP_STORAGE_ADMIN. */
|
||||||
static constexpr uint64_t SYS_FS_GROW = 208; // (drive, FsGrowStatus*, apply) -> 1 / 0 / status
|
static constexpr uint64_t SYS_FS_GROW = 208; // (drive, FsGrowStatus*, apply) -> 1 / 0 / status
|
||||||
|
|
||||||
|
/* Time.hpp -- wall clock as UTC unix seconds (SYS_GETTIME is local time). */
|
||||||
|
static constexpr uint64_t SYS_GETUNIXTIME = 209; // () -> seconds
|
||||||
|
|
||||||
/* Kernel-owned process capabilities. User identities may namespace
|
/* Kernel-owned process capabilities. User identities may namespace
|
||||||
per-user resources, but never participate in authorization decisions. */
|
per-user resources, but never participate in authorization decisions. */
|
||||||
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
static constexpr uint64_t CAP_PROCESS_ADMIN = 1ULL << 0;
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ extern "C" {
|
|||||||
#define MTK_SYS_WIN_DRAG_FORMATS 206
|
#define MTK_SYS_WIN_DRAG_FORMATS 206
|
||||||
#define MTK_SYS_WIN_DRAG_GET_DATA 207
|
#define MTK_SYS_WIN_DRAG_GET_DATA 207
|
||||||
#define MTK_SYS_FS_GROW 208
|
#define MTK_SYS_FS_GROW 208
|
||||||
|
#define MTK_SYS_GETUNIXTIME 209
|
||||||
/* @SYSCALLS-END */
|
/* @SYSCALLS-END */
|
||||||
|
|
||||||
/* Syscall status codes (montauk/statuscodes.h for C++). */
|
/* Syscall status codes (montauk/statuscodes.h for C++). */
|
||||||
@@ -805,6 +806,10 @@ static inline void mtk_gettime(mtk_datetime *out) {
|
|||||||
_mtk_syscall1(MTK_SYS_GETTIME, (long)out);
|
_mtk_syscall1(MTK_SYS_GETTIME, (long)out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int64_t mtk_get_unix_time(void) {
|
||||||
|
return (int64_t)_mtk_syscall0(MTK_SYS_GETUNIXTIME);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int mtk_set_unix_time(int64_t unix_seconds) {
|
static inline int mtk_set_unix_time(int64_t unix_seconds) {
|
||||||
return (int)_mtk_syscall1(MTK_SYS_SETUNIXTIME, (long)unix_seconds);
|
return (int)_mtk_syscall1(MTK_SYS_SETUNIXTIME, (long)unix_seconds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,42 +51,6 @@ inline uint32_t read_be32(const uint8_t* p) {
|
|||||||
((uint32_t)p[2] << 8) | (uint32_t)p[3];
|
((uint32_t)p[2] << 8) | (uint32_t)p[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void write_be64(uint8_t* p, uint64_t value) {
|
|
||||||
for (int i = 7; i >= 0; i--) {
|
|
||||||
p[i] = (uint8_t)value;
|
|
||||||
value >>= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool is_leap_year(int year) {
|
|
||||||
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int days_in_month(int month, int year) {
|
|
||||||
static const int days[] = {0, 31, 28, 31, 30, 31, 30,
|
|
||||||
31, 31, 30, 31, 30, 31};
|
|
||||||
return month == 2 && is_leap_year(year) ? 29 : days[month];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int64_t current_unix_seconds() {
|
|
||||||
montauk::abi::DateTime now = {};
|
|
||||||
montauk::gettime(&now);
|
|
||||||
if (now.Year < 1970 || now.Month < 1 || now.Month > 12 ||
|
|
||||||
now.Day < 1 || now.Day > days_in_month(now.Month, now.Year))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
int64_t days = 0;
|
|
||||||
for (int year = 1970; year < (int)now.Year; year++)
|
|
||||||
days += is_leap_year(year) ? 366 : 365;
|
|
||||||
for (int month = 1; month < (int)now.Month; month++)
|
|
||||||
days += days_in_month(month, now.Year);
|
|
||||||
days += now.Day - 1;
|
|
||||||
|
|
||||||
int64_t local = days * 86400 + (int64_t)now.Hour * 3600 +
|
|
||||||
(int64_t)now.Minute * 60 + now.Second;
|
|
||||||
return local - (int64_t)montauk::gettz() * 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool same_bytes(const uint8_t* a, const uint8_t* b, int count) {
|
inline bool same_bytes(const uint8_t* a, const uint8_t* b, int count) {
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if (a[i] != b[i]) return false;
|
if (a[i] != b[i]) return false;
|
||||||
@@ -115,14 +79,15 @@ inline int synchronize(const char* server, uint32_t timeout_ms = 5000,
|
|||||||
request[2] = 6; // poll exponent: 2^6 = 64 seconds
|
request[2] = 6; // poll exponent: 2^6 = 64 seconds
|
||||||
request[3] = 0xEC; // precision: approximately 2^-20 seconds
|
request[3] = 0xEC; // precision: approximately 2^-20 seconds
|
||||||
|
|
||||||
int64_t unix_now = current_unix_seconds();
|
// The transmit timestamp only has to come back in the reply's origin
|
||||||
if (unix_now <= 0) {
|
// field, so send a random cookie rather
|
||||||
|
// than the local clock, which may still be invalid before the first sync.
|
||||||
|
uint8_t cookie[8];
|
||||||
|
if (montauk::getrandom(cookie, sizeof(cookie)) != (int64_t)sizeof(cookie)) {
|
||||||
montauk::closesocket(fd);
|
montauk::closesocket(fd);
|
||||||
return CLOCK_ERROR;
|
return CLOCK_ERROR;
|
||||||
}
|
}
|
||||||
uint64_t transmit_timestamp =
|
for (int i = 0; i < 8; i++) request[40 + i] = cookie[i];
|
||||||
((uint64_t)unix_now + UNIX_EPOCH_DELTA) << 32;
|
|
||||||
write_be64(request + 40, transmit_timestamp);
|
|
||||||
|
|
||||||
progress(progress_callback, "Sending NTP request...");
|
progress(progress_callback, "Sending NTP request...");
|
||||||
if (montauk::sendto(fd, request, sizeof(request), server_ip, SERVER_PORT) < 0) {
|
if (montauk::sendto(fd, request, sizeof(request), server_ip, SERVER_PORT) < 0) {
|
||||||
|
|||||||
@@ -505,6 +505,8 @@ namespace montauk {
|
|||||||
|
|
||||||
// Timekeeping (wall-clock)
|
// Timekeeping (wall-clock)
|
||||||
inline void gettime(montauk::abi::DateTime* out) { syscall1(montauk::abi::SYS_GETTIME, (uint64_t)out); }
|
inline void gettime(montauk::abi::DateTime* out) { syscall1(montauk::abi::SYS_GETTIME, (uint64_t)out); }
|
||||||
|
// UTC seconds since 1970 (gettime() is local time).
|
||||||
|
inline int64_t get_unix_time() { return syscall0(montauk::abi::SYS_GETUNIXTIME); }
|
||||||
inline int set_unix_time(int64_t unix_seconds) {
|
inline int set_unix_time(int64_t unix_seconds) {
|
||||||
return (int)syscall1(montauk::abi::SYS_SETUNIXTIME, (uint64_t)unix_seconds);
|
return (int)syscall1(montauk::abi::SYS_SETUNIXTIME, (uint64_t)unix_seconds);
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -196,17 +196,12 @@ void free_trust_anchors(TrustAnchors* tas) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void get_bearssl_time(uint32_t* days, uint32_t* seconds) {
|
void get_bearssl_time(uint32_t* days, uint32_t* seconds) {
|
||||||
montauk::abi::DateTime dt;
|
// Certificate validity is UTC; BearSSL counts days from 0000-01-01,
|
||||||
montauk::gettime(&dt);
|
// and 1970-01-01 is day 719528.
|
||||||
int y = dt.Year, m = dt.Month, d = dt.Day;
|
int64_t t = montauk::get_unix_time();
|
||||||
uint32_t total = 365u * (uint32_t)y
|
if (t < 0) t = 0;
|
||||||
+ (uint32_t)((y+3)/4) - (uint32_t)((y+99)/100) + (uint32_t)((y+399)/400);
|
*days = 719528u + (uint32_t)(t / 86400);
|
||||||
const int md[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
|
*seconds = (uint32_t)(t % 86400);
|
||||||
for (int mo = 1; mo < m && mo <= 12; mo++) total += md[mo];
|
|
||||||
if (y%4==0 && (y%100!=0 || y%400==0) && m > 2) total++;
|
|
||||||
total += d - 1;
|
|
||||||
*days = total;
|
|
||||||
*seconds = (uint32_t)(dt.Hour*3600 + dt.Minute*60 + dt.Second);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tls_send_all(int fd, const unsigned char* data, size_t len) {
|
int tls_send_all(int fd, const unsigned char* data, size_t len) {
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ static bool load_settings(char* server, int server_cap) {
|
|||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool reached_server(int result) {
|
||||||
|
return result == montauk::ntp::TIMEOUT ||
|
||||||
|
result == montauk::ntp::INVALID_REPLY ||
|
||||||
|
result == montauk::ntp::SERVER_REFUSED;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" void _start() {
|
extern "C" void _start() {
|
||||||
log("service started");
|
log("service started");
|
||||||
|
|
||||||
@@ -68,16 +74,21 @@ extern "C" void _start() {
|
|||||||
|
|
||||||
montauk::abi::NetCfg net = {};
|
montauk::abi::NetCfg net = {};
|
||||||
montauk::get_netcfg(&net);
|
montauk::get_netcfg(&net);
|
||||||
|
// No lease yet (DHCP may still be waiting for link): keep polling
|
||||||
|
// instead of backing off, so the first sync lands right after DHCP.
|
||||||
if (net.ipAddress == 0 || net.dnsServer == 0) {
|
if (net.ipAddress == 0 || net.dnsServer == 0) {
|
||||||
next_attempt = now + RETRY_INTERVAL_MS;
|
|
||||||
montauk::sleep_ms(CONFIG_POLL_MS);
|
montauk::sleep_ms(CONFIG_POLL_MS);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = montauk::ntp::synchronize(server);
|
int result = montauk::ntp::synchronize(server);
|
||||||
log(montauk::ntp::result_string(result));
|
log(montauk::ntp::result_string(result));
|
||||||
next_attempt = montauk::get_milliseconds() +
|
// The server rate limit only applies once a request reached it;
|
||||||
(result == montauk::ntp::OK ? SYNC_INTERVAL_MS : RETRY_INTERVAL_MS);
|
// failures before sending (DNS, socket) retry at the poll interval.
|
||||||
|
uint64_t delay = SYNC_INTERVAL_MS;
|
||||||
|
if (result != montauk::ntp::OK)
|
||||||
|
delay = reached_server(result) ? RETRY_INTERVAL_MS : CONFIG_POLL_MS;
|
||||||
|
next_attempt = montauk::get_milliseconds() + delay;
|
||||||
montauk::sleep_ms(CONFIG_POLL_MS);
|
montauk::sleep_ms(CONFIG_POLL_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user