Page MenuHomePhabricator

sclockadj fingerprinting defense - set time using sclockadj the same way NTP / sntp / chrony / systemd-timesyncd is changing the clock
Open, NormalPublic

Details

Impact
Normal

Event Timeline

Patrick triaged this task as Normal priority.Jul 25 2018, 7:22 AM
Patrick created this task.

the time could be set with timedatectl by feeding it the time with this command:

set-time [TIME]

https://www.freedesktop.org/software/systemd/man/timedatectl.html

Currently time is set using gnu date (clock jump) (initial run after current boot) or sclockadj (consecutive run) (slow clock adjustment).

timedatectl set-time seems to be doing the same as gnu date, i.e. just a clock jump or did I oversee something?

We'll have figure out what systemd-timesyncd is using to adjust the clock and then figure out if we can call that from sdwdate.

It doesn't seem that timedatectl supports gradual time adjustment. Our next best option is ntpd which can do so but cannot coexist with timedatectl - we can only run either but not both. According to popcon, ntpd is the mos widely used time daemon so its the natural choice.

https://www.digitalocean.com/community/tutorials/how-to-set-up-time-synchronization-on-ubuntu-16-04

Since we are interested in ntpd's default behavior (for blending in purposes) it turns out that it performs instant clock jumps once the delta difference is excessively large otherwise its slewing algorithm would take forever to adjust the time.

https://stackoverflow.com/questions/33746324/ntp-questions-slewing-versus-jumping-the-time

ntp doesn't want to jump your clock, because discontinuous time jumps are Bad. It wants to adjust your clock gradually -- very gradually. It's very conservative: by default, it won't slew your clock by more than xx parts per million (ppm).

But since ntp is so conservative, if it finds out that your clock is too far off, such that adjusting it gradually would take forever, it will fall back and jump your clock anyway (even though that's Bad). It does this, by default, if it would take longer than yy to adjust your clock gradually.

For default ntpd:

xx: 128ms

The ntpd algorithms discard sample offsets exceeding 128 ms, unless the interval during which no sample offset is less than 128 ms exceeds 900s.

yy: 600s

In practice, the need for a step has been extremely rare and almost always the result of a hardware failure or operator error. The step threshold and stepout threshold can be changed using the step and stepout options of the tinker command, respectively. If the step threshold is set to zero, the step function is entirely disabled and the clock is always slewed. The daemon sets the step threshold to 600 s using the -x option on the command line.


Now with this info in mind, what is the best way to go about this? I think sudden clock jumps done by sdwdate directly are acceptable and less complex than relying on another time daemon that essentially does the same thing.

It doesn't seem that timedatectl supports gradual time adjustment.

systemd-timesyncd does support gradual time adjustment.

https://github.com/systemd/systemd/blob/master/src/timesync/timesyncd-manager.c#L247

/*
 * For small deltas, tell the kernel to gradually adjust the system
 * clock to the NTP time, larger deltas are just directly set.
 */

(But even if it wasn't gradually adjusting he clock but was Debian's default we could still consider it nonetheless.)


Since we are interested in ntpd's default behavior (for blending in purposes) it turns out that it performs instant clock jumps once the delta difference is excessively large otherwise its slewing algorithm would take forever to adjust the time.

Emphasis is mine. And "once the delta difference is excessively" is the crucial point.

I think sudden clock jumps done by sdwdate directly are acceptable and less complex than relying on another time daemon that essentially does the same thing.

  • NTP: uses only clock jumps once the delta difference is excessive, otherwise gradual adjustment
  • sdwdate: therefore should do the same and not only clock jump (otherwise it's fingerprintable)

From what I understand, this code path is only relevant when timesyncd is talking directly with NTP servers and reacting to replies about deltas between local and remote times. There is no way you can call that function from the command line when using timedatectl standalone AFAICT.

The only option here is to include this C code snippet in our sclockadj with the variables changed appropriately so sclockadj can call the kernel directly and do this operation by itself.

Another option is to look into ntpd and see if it can do these operations without it needing to contact servers.

The easy way: calculating the offset between local time and the onion average in timesync then using ntpdate's slew option if the offset is less than 0.5s. Otherwise you tell it to step up the time immediately so that you are accurately mimicking the default behavior. However you can force slewing all the time with -B. This way you won't need to touch kernel syscalls as ntpdate should be able to do the operation for you.

https://man.cx/ntpdate(1)

The ntpdate utility makes time adjustments in one of two ways. If it determines that your clock is off by more than 0.5 seconds it simply steps the time by calling gettimeofday(3C). If the error is less than 0.5 seconds, by default, it slews the clock’s time with the offset, by way of a call to adjtime(2). The latter technique is less disruptive and more accurate when the offset is small; it works quite well when ntpdate is run by cron every hour or two. The adjustment made in the latter case is actually 50% larger than the measured offset. This adjustment tends to keep a badly drifting clock more accurate, at some expense to stability. This tradeoff is usually advantageous. At boot time, however, it is usually better to step the time. This can be forced in all cases by specifying the -b option on the command line.

-b

Step the time by calling gettimeofday(3C).

-B

Force the time to always be slewed using the adjtime(2) system call, even if the measured offset is greater than +-128 ms. The default is to step the time using settimeofday(3C) if the offset is greater than +-128 ms. If the offset is much greater than +-128 ms in this case, that it can take a long time (hours) to slew the clock to the correct value. During this time the host should not be used to synchronize clients.


We need to blackhole network access for ntpdate. Is this easy?

Yes, not readily accessible from command line.

The only option here is to include this C code snippet in our sclockadj with the variables changed appropriately so sclockadj can call the kernel directly and do this operation by itself.

Yes. Something like that.

Perhaps we could link that code? (In bash terms source.) i.e. make sclockadj call that function without having to copy/paste it to our sclockadj. If that is not (reasonably) doable (for example if that code is too systemd specific / too intermingled with systemd) we could also copy/paste that code snippet into sclockadj. Either way, on each major Debian upgrade we would have to check how their code changed but I guess it will change very very little.

Another option is to look into ntpd and see if it can do these operations without it needing to contact servers.

Yes. Similarly if we could pinpoint ntp's time setting C code that could also be linked or copy/pasted for reuse in sclockadj.

/usr/sbin/ntpdate as far as I know doesn't accept a command line command to take an offset (or anything). It connects to remote servers in its default design.

In theory, we could make sdwdate provide a local (default) (or optional opt-in server) NTP compatible time provider. Could be useful anyhow. -> sdwdate-server No idea how hard that would be.

And then configure NTP to connect only to that local NTP server.

We need to blackhole network access for ntpdate. Is this easy?

Sounds like a very doable part. NTP runs under user NTP anyhow so that user could be firewalled. (NTP is somewhat firewalled anyhow since it's using UDP.)

In theory, we could make sdwdate provide a local (default) (or optional opt-in server) NTP compatible time provider. Could be useful anyhow. -> sdwdate-server No idea how hard that would be.

And then configure NTP to connect only to that local NTP server.

I had the same idea at first but then it gets much more complex as I thought about it. Having the NTP client communicate over localhost is the easier part, however talking to the client would require sdwdate-server to talk NTP - a huge task. A way around that is to also have an offline NTP server which sdwdate then feeds time data that in turn relays it to the NTP client whenever it asks for it and then sets time accordingly - but then I don't see any way we can arbitrarily feed time to the NTP server.

Perhaps we could link that code? (In bash terms source.) i.e. make sclockadj call that function without having to copy/paste it to our sclockadj.

Looks like the most practical solution and one where we can switch out code to support the more popular daemon in the future.

Yes. Similarly if we could pinpoint ntp's time setting C code that could also be linked or copy/pasted for reuse in sclockadj.

Here it is but I think its more complicated than timesyncd's code. might as well stick with the latter because its the most widely used now.

https://github.com/ntp-project/ntp/blob/1a399a03e674da08cfce2cdb847bfb65d65df237/libntp/adjtime.c