ntpd

新型肺炎が猛威を奮っている。厚生労働省の水際作戦も失敗に終わり、隔離船で増殖とか、一体どうなってるの状態。

屋形船での新年会で菌を貰った人、さぞかし一期一会を実感している事でしょう。 でも、よくここまで突き止めたな。きっと裏で、警視庁の刑事が大量に投入されて、聞き込みに 回っていたに違いない。彼らは、こういうの得意中の得意ですから。

でもね、人海戦術もいいけど、もっと科学的に出来ないかねぇと思うこの頃。 んな訳で、シュミレーションとか無いの? 毎度こういうのはAIにお任せもあれなんでね。 探してみたら

数理モデルとシミュレーション AI

こんなのが出て来た。そうかmatlabを使えば簡単なんだな。(世の中では凄くpython菌が増殖してて、バンデミックになってますが、そういうのにはオイラーは感染しないからね)

貧者のmatlabが有ったな。 えーと、 maxima じゃなくて octave だな。 試してみるか。

こちらは、有名なローレンツ アトラクタ

#lorentz.m
function dx = lorentz(x,t)
    s=10; R=28; b=8/3;
    dx(1) = -s * ( x(1) - x(2) );
    dx(2) = R * x(1) - x(2) -x(1) * x(3);
    dx(3) = x(1) * x(2) - b * x(3);
endfunction

t=linspace(0, 30, 3000);
x=lsode("lorentz",[0; 0.03; 0],t);
plot3( x(:,1), x(:,2), x(:,3));

2070年問題

前回はOpenBSDの起動時に参照されるRTCチップからデータを呼び出し、epocに変換する所を見た。チップは西暦年の下2桁しか保持してない。そこで、その数値が70以下なら2000年代。それを超えていたら1900年代として区別してた。同様なロジックはdateコマンドでも使われていた。

これって2070年問題じゃん。折角epocを64Bitにしたって、こういう問題に足を引っ張られていては、元の木阿弥である。

まてまて、2070年まで、そんな旧態依然のチップが使われたパソコンが残って(しかも現役で)使われているだろうか?

今流行りのスマホでは、そんな問題をさっぱり聞いた事ないぞ。それもそのはず、時間情報は電波に載って届くから、スマホ内にそんなチップは収納されていないんだ。

パソコンでそれをやろうとすると、ドコモ支給のパソコンとかAUと契約してとか、ややこしい話になる。んで、そんな面倒を避ける為、ntpが利用されている。これからは必須の技術なんで、是非この機会に習得しておこう。来る2070問題を避けるためにもね。(ジョークなんで真に受けないように!)

ntpctlとntpd.conf

ntpdを十分に動かして、エージングが完了すると、下記のような状態になる。

ob$ ntpctl -s all
5/5 peers valid, constraint offset -2s, clock synced, stratum 2

peer
   wt tl st  next  poll          offset       delay      jitter
162.159.200.123 time.cloudflare.com
    1 10  3 1018s 1555s        -2.145ms    27.188ms     1.125ms
162.159.200.1 from pool pool.ntp.org
    1 10  3 1118s 1611s        -1.134ms    27.788ms     3.358ms
103.202.217.106 from pool pool.ntp.org
    1 10  2 1000s 1568s         2.807ms    41.823ms     3.876ms
133.243.238.163 from pool pool.ntp.org
 *  1 10  1 1022s 1592s         2.638ms    19.568ms     2.089ms
195.50.171.101 from pool pool.ntp.org
    1 10  2  897s 1500s         5.596ms   274.167ms    26.054ms

安定しているので、poll間隔も当初の30秒から大幅に伸びている。贅沢な事に1次標準に同期して、このマシンの時間精度は2次標準と見做せるよと、お墨付きを頂いた。目出度い事である。

/etc/ntpd.conf

servers pool.ntp.org
server time.cloudflare.com
sensor *
constraints from "https://www.google.com"

これがデフォルトの設定。時間の元を貰ってくるのは、pool.ntp.orgとtime.cloudflare.comからである。どれかが死んでいても大丈夫と言う、安全対策、冗長系になっている。

sensorってのが有るけど、これは手元に、正確な時刻発生装置が有る場合に使える。その手の設備と言うかデバイスで代表的なものは、GPSである。GPSってカーナビとか、今ではスマホにも付いているやつの心臓部だ。正確な時計。時間を距離に換算する仕組みなんで、複数の人工衛星から届く時間を元に位置を決定するのがカーナビの原理。

民間で手に入る、もっとも正確な時計だろう。雑に言うと高精度の電波時計ね。素人が使う(うちにも有るような)電波時計は、

日本では「JJY」と呼ばれる標準電波の送信局があり、福島県大鷹鳥谷山のおおたかどや山
標準電波送信所(送信周波数40kHz)と、福岡県と佐賀県との県境に位置する羽金山の
はがね山標準電波送信所(送信周波数60kHz)の2つの送信所からの電波を受信する

こちらの方ね。

GPS受信型NTPサーバ とか GPSタイムサーバー TSV-400GP こんな製品が有るようだ。 あっ、GPS装置はサツも使うし浮気調査にも使われる。そのためのレンタル品まで用意されてた。恐い世の中ですよ。

恐いと言えば、偽のntpサーバーを設置されて、それを利用させられてしまうと、アリバイ工作に加担させられてしまう。ntpプロトコルでは、それを防止するすべが無いので、constraintsってのが導入されている。

上の設定では、制約にググルのサーバーが指定されている。ここからの時間情報から大幅にずれていたら、良からぬ事(偽物時刻をつかまされている)が起こっているよってんで、時間同期を拒否するようになっている(そうだ)。

constraints

上でググルにアクセスして、時刻を得ているって説明が有ったけど、そんな事出来るの? ググルにアクセスすると、普段見慣れた入力画面しか出てこないんだけど。

まてまて、そんな素人みたいな事を言うな。この世界に何年住んでいるんだい! 思うに、きっとhttpのヘッダーに時間情報が載っているんだろう。検証してみる。

ob$ TZ=GMT date; w3m -dump_head https://www.google.com >zz
Tue Feb 18 21:22:58 GMT 2020
ob$ less zz
HTTP/1.0 200 OK
Date: Tue, 18 Feb 2020 21:23:26 GMT
 :
Set-Cookie: 1P_JAR=2020-02-18-21; expires=Thu, 19-Mar-2020 21:23:26 GMT; path=/; domain=.google.com; Secure

ローカル時間(OpenBSDが時を刻んでいるやつ)をGMTで表示(何たって世界共通時間ですから)、次にw3mを使ってヘッダー情報だけを採取。後は、ヘッダーから、時間関連のタグを抜き出してみた。クッキーの方は、ぐぐるの心持ち次第で変えられちゃう可能性が有るんで、使うとしたらDateタグの情報だな。

ob$ TZ=GMT date; w3m -dump_head https://www.yahoo.co.jp
Tue Feb 18 21:56:44 GMT 2020
HTTP/1.0 200 OK
Date: Tue, 18 Feb 2020 21:57:12 GMT

みんなのニュースであるやっほーも、今日の日付を返してくれているね。

w3mが無い環境だと、pythonで我慢だな。

o32$ cat aa.py
import urllib.request
response = urllib.request.urlopen('https://www.google.com')
print(response.info())
o32$ TZ=gmt date; python3 aa.py

ここまで推測出来れば、後はソースに当たるだけ。

ob$ grep Date *.[ch]
constraint.c:           if (strcasecmp("Date:", line) != 0)
constraint.c:    * TLS handshake, based on the time specified by the server's HTTP Date:
constraint.c:   /* Report parsed Date: as "received time" */

ソースを雑見すると、ヘッダー情報の改変を心配してhttpsしか受け付けない、そしてアクセスしたサイトがほんまもんかdnsの検証機能を使って検証してるっぽい。ぽいなので、余り信用しないでね。今思い付いたぞ、httpsは暗号化も大事だけど、身元保証人制度が根本にあるから、こちらの機能を使っているんだろう。

ntpd

VMwareに入れているOpneBSD(amd64)で走らせてみる。例によって、-dでフォーグラウンド動作、-sでntpd起動時に、強制時刻合わせ。

/etc/rc.confのntpd起動フラグを確認すると、何も指定されていない。(NOに設定すると、起動するなの意思表示になる)すなわち、デフォの指定になるから、2つの機能は無しで起動って事になる。普通は起動時に、一度だけ強制時刻合わせをするのが流儀だと思うんだけど、あえてそれをしていない。深遠な理由が有るのだろうか。

ob$ doas ntpd -d -s
ntp engine ready
set local clock to Wed Feb 19 07:18:23 JST 2020 (offset -0.001565s)
constraint reply from 172.217.175.68: offset -0.923849
peer 162.159.200.1 now valid
peer 162.159.200.123 now valid
peer 133.243.238.243 now valid
peer 162.159.200.1 now valid
peer 211.19.59.28 now valid
adjusting local clock by -0.328642s
clock is now synced
constraint reply from 172.217.175.4: offset -0.169164
adjusting clock frequency by -4.992957 to 18.582043ppm
adjusting clock frequency by 0.406461 to 18.988504ppm

暫くして、動作確認した。

ob$ ntpctl -s all
5/5 peers valid, 1/1 sensors valid, constraint offset -1s, clock synced, stratum 2

peer
   wt tl st  next  poll          offset       delay      jitter
162.159.200.1 time.cloudflare.com
    1 10  3   13s   34s        -3.343ms    24.397ms     4.601ms
162.159.200.1 from pool pool.ntp.org
    1 10  3   22s   30s        -3.163ms    24.624ms     2.089ms
133.243.238.243 from pool pool.ntp.org
    1 10  1   22s   30s         1.227ms    19.334ms     2.686ms
211.19.59.28 from pool pool.ntp.org
 *  1 10  1    4s   34s         1.127ms    22.873ms     4.293ms
162.159.200.123 from pool pool.ntp.org
    1 10  3    7s   33s        -1.381ms    28.804ms    12.642ms

sensor
   wt gd st  next  poll          offset  correction
vmt0
    1  1  0    6s   15s       308.702ms     0.000ms

ちゃんと同期してるね。但し確認時間間隔(poll)は短い時間になっている。サーバーさんにご負担かけて、どうもすみません状態だな。

VMT(4)                       Device Drivers Manual                      VMT(4)

NAME
     vmt - VMware Tools driver

DESCRIPTION
     The vmt driver is a kernel level implementation of VMware Tools.  VMware
     Tools are intended to provide better support for operating systems
     running inside virtual machines.

     provides access to the host machine's clock as a timedelta sensor.

sensorに見慣れないデバイスが使われていたので確認。VMwareさんからの回し者だな。なんて思っていたら、OpenBSDも認めるdriver扱い。4.4の時代からお仲間になったそうなんで、由緒ある者認定って事で宜しいかな。

それはそうと、vmtデバイスからの時刻オフセットが気のせいか大きいな。これはきっとVMwareの上流に位置するWindows10の時刻合わせがサボっていて真面目にやらないからだろう。素人なんてそんな事気にしないってMSが見くびっているに違いない。

暇な時に、Windows10の時刻合わせを頻繁にやってみよう。えーと、どうやればいいんだ。ぐぐってみろよ。 Windows 10で手動で時刻合わせを行なう方法これか。面倒っぽい。だからGUIは嫌いよ。

ntpd サーバーを受け継ぐ

VMwareの支援が有るなんて事を知ったものだから、それに頼ってみる。設定は簡単。/etc/ntpd.confのserver(s)の所をコメントアウトするだけ。

o32$ ntpctl -s all
1/1 sensors valid, constraint offset -2s, clock synced, stratum 1

sensor
   wt gd st  next  poll          offset  correction
vmt0
 *  1  1  0    9s   15s         3.769ms     0.000ms

peerが無くなって、時刻はvmtだけから供給されるようになった。一次サーバーに昇格ですよ。やったね。

でもね、Windows10の時計合わせがサボっている為、全く信用なりませんけど。上の方で、wgetしたぐぐるサーバーとdateコマンドを叩いた時のずれは、糞Windowsの時計合わせがさぼっていた為だった。時計合わせをしたら、両者は一致したよ。

次なるは、ゲストOSを動かして、その上でホスト側からの時刻を貰って来るように設定。この場合は、ゲスト側のntpd.confに、ホスト側のIPを登録するだけ。

vm$ ntpctl -s all
4/5 peers valid, constraint offset -1s, clock synced, stratum 2

peer
   wt tl st  next  poll          offset       delay      jitter
XXX.XXX.XXX.XXX
    1  2  -    0s    0s             ---- peer not valid ----
82.193.117.90 from pool pool.ntp.org
    1 10  1   12s   34s         0.825ms   350.944ms    42.466ms
62.116.162.126 from pool pool.ntp.org
    1 10  2   10s   32s        19.180ms   310.747ms    29.266ms
216.197.156.83 from pool pool.ntp.org
 *  1 10  1   12s   34s        16.104ms   235.022ms    37.397ms
199.182.204.197 from pool pool.ntp.org
    1 10  2   12s   34s        31.117ms   174.105ms    26.622ms

伏字がホスト側なんだけど、全く通信出来ていない。IP直の設定だと、信用ならんて事なんだろうね。IPだけじゃ身元保証人が同定出来ませんからね。

vmt

vmtでの受け継ぎが上手くいかないものだから、不貞腐れvmtのコードを見る事にした。ドライバーだよって説明が有ったので、devの中を探したら、pv/vmt.cに見つかった。 コードが長くて読み切れないので、コメントを拾い読み。

 * Protocol reverse engineered by Ken Kato:
 * https://sites.google.com/site/chitchatvmback/backdoor

 * Notes on tracing backdoor activity in vmware-guestd:
 *
 * - Find the addresses of the inl / rep insb / rep outsb
 *   instructions used to perform backdoor operations.
 *   One way to do this is to disassemble vmware-guestd:
 *
 *   $ objdump -S /emul/freebsd/sbin/vmware-guestd > vmware-guestd.S
 *
 *   and search for '<tab>in ' in the resulting file.  The rep insb and
 *   rep outsb code is directly below that.
 *
 * - Run vmware-guestd under gdb, setting up breakpoints as follows:
 *   (the addresses shown here are the ones from VMware-server-1.0.10-203137,
 *   the last version that actually works in FreeBSD emulation on OpenBSD)

 * break *0x805497b   (address of 'in' instruction)
 * commands 1
 * silent
 * echo INOUT\n
 * print/x $ecx
 * print/x $ebx
 * print/x $edx
 * continue
 * end

backdoorなんてURLが恐いぞ。用心して行ってみると、日本人が開設された場所だった。みんなでリバースエンジニアリングして、あちこちのOSに移植しましょって趣旨。

上のコメントにも有る通り、リバースのやり方まで解説されてる。こういうの楽しいんだよな。 自宅で居ながらにして情報が手に入る。新型肺炎菌が充満してる都会に出かけなくて、リモートワークしながらってのが、超現代風ですよ。

ntp.h and ntpd.h

余勢を駆ってntpdのソースを閲覧。ぼーと眺めていると、あの子に怒られそうなので、取り合えず目標を立てる。 まずは、-sを付けて起動した時、どんな風に強制時刻合わせをやってるの? 次の目標は、身元保証人との時差が大きすぎると同期を中止するって言うけど、その許容範囲は?

定石に則りヘッダーファイルを見る。ntp.hとntpd.hだな。

RFC2030だよ、データのレイアウトはこうだよってのがntp.hに書いてあった。ntpd.hには、許容範囲が定義されてると思ったんだけど、それらしいのは見つからず。でも、どのファイルにどんな関数が有るかは定義されてた。ntp.cにoffset_compareなんてのが有るなあ。プチ気になる。

ntp.c

o32$ grep log_ ntp.c
         :
        log_info("ntp engine ready");
         :
                                log_info("clock is now synced");
                                log_info("clock is now unsynced");

こんなのが見られるので、実働部隊っぽい。期待してた、offset_compareは期待外れだった。文字列のcompreを時間のcompreに置き換えたものだったよ。

範囲拡大

一つのファイルに執着していても意味無いので、探索範囲を拡大。犬も歩けば棒に当たる作戦発動だな。

client.cに面白いコメントを発見

         * From RFC 2030 (with a correction to the delay math):
         *
         *     Timestamp Name          ID   When Generated
         *     ------------------------------------------------------------
         *     Originate Timestamp     T1   time request sent by client
         *     Receive Timestamp       T2   time request received by server
         *     Transmit Timestamp      T3   time reply sent by server
         *     Destination Timestamp   T4   time reply received by client
         *
         *  The roundtrip delay d and local clock offset t are defined as
         *
         *    d = (T4 - T1) - (T3 - T2)     t = ((T2 - T1) + (T3 - T4)) / 2.

これがntpの本質だな。図に書いてみると良く分かる。電波のスピードは30万km/sec、銅線の中だと誘電率の関係でスピードが落ちてしまい、20から25ぐらいになるかな。それにルーターとかのハンドリング時間も有るしね。上記はそれを考慮した式だ。

後は目が眩んで、おぼろですよ。んな事で、gdbに頼る事を思い付いた。結局それかいってのは、置いておいて。

run ntpd

恒例のソース一式をtmpに持ってきて自前で走らせる術。Makefileに -gを忘れず追加。

o32$ doas ./ntpd -d -s
ntpd: start_child: execvp: No such file or directory
Terminating
o32$ doas /tmp/ntpd/ntpd -d -s
ntp engine ready
set local clock to Thu Feb 20 08:14:22 JST 2020 (offset 0.017677s)
ntpd: start_child: execvp: No such file or directory
^Cntp engine exiting
Terminating

OpenBSDのきつい監査に引っかかって、相対Pathでは起動出来ず。絶対Path指定したら、走り始めたけど、まだ不足品が有るな。

期待値は

o32$ doas ntpd -d -s
ntp engine ready
set local clock to Thu Feb 20 08:23:08 JST 2020 (offset 0.002892s)
constraint reply from 172.217.175.68: offset -0.869282
peer 133.243.238.243 now valid
peer 133.243.238.163 now valid
^Cntp engine exiting
Terminating

こんな感じね。

この時に起動してるプロセスは、こんな感じ

47515 p1  I<+pU    0:00.07 ntpd -s -d
96513 p1  S<+p     0:00.09 ntpd: ntp engine (ntpd)
47762 p1  I+p      0:00.04 ntpd: dns engine (ntpd)

親子関係が生まれている。pid=47515が親で、他は子供。rootの特権が強すぎて超危険なので、権限を押さえた子供に仕事を任せている。ntpは時刻合わせ。dnsは安全機構を施したやつだろう。

ntpd.cのソースを見ると、ntpdの設置場所は、/usr/sbin/ntpdだよって固定してる。変な所(上でやったのは正にこれに当たる)に設置した親は、子供が仕事にかかれないように用心してるんだ。 隙が無いな。

そんな訳なんで、その場所を見てる所に手を入れて無視させるか、debug機能を盛り込んだntpdを正規の場所に置くか(元々のやつは保存しとく)の、2択になる。今回は後者を選択する。

o32$ wc *.[ch]
     527    1766   14400 client.c
     183     579    4138 config.c
    1152    3337   27443 constraint.c
     450    1187   10207 control.c
     202     517    3636 log.c
      49     249    1803 log.h
     865    2426   21055 ntp.c
     149     705    5284 ntp.h
     250     691    5496 ntp_dns.c
      71     285    1886 ntp_msg.c
     916    2549   20882 ntpd.c
     427    1232   11388 ntpd.h
     251     758    5994 sensors.c
     203     649    5639 server.c
     237     635    4540 util.c
    5932   17565  143791 total

こんな規模になるんで、ETAGSを作っちゃった。

長くなりそうなので、(たぶん)次回に続く。