PR #188 Fix stopping during greeting on some POPs servers について

171 views
Skip to first unread message

Masamichi Hosoda

unread,
Mar 6, 2024, 1:20:04 AM3/6/24
to mew...@googlegroups.com, ka...@iij.ad.jp, true...@trueroad.jp
山本様、皆様

細田です。
おせわになっております。

PR #188 Fix stopping during greeting on some POPs servers
について、お恥ずかしながら私の英語力があやしいところもあり、
日本語で補足説明させていただければと思います。

PR で最初にお出ししたパッチは
他の環境を十分考慮できておらず申し訳ありませんでした。
これは完全に破棄して昨日まったく別の修正を force push しています。

昨日修正した現在の PR では
・平文 POP (ポート 110)
・GnuTLS で POP over TLS/SSL (ポート 995)
・GnuTLS で STARTTLS (ポート 110)
・stunnel で POP over TLS/SSL (ポート 995)
・stunnel で STARTTLS (ポート 110)
いずれでも動作することを確認しております。

これをマージしていただけるとありがたいと思っております。
以下、発生事象と解析した内容などを書かせていただきます。

・発生事象

Mew で GnuTLS を使用して POP over TLS/SSL で
secure.plala.or.jp:995
へ接続するとパスワードを聞かれる前に止まってしまいます。

#ぷららユーザが使用する標準的な POP over TLS/SSL サーバです。
#つまりほとんどのぷららユーザが影響を受けていると考えています。

再現用の設定とその際の Mew-debug は
https://github.com/kazu-yamamoto/Mew/pull/188#issuecomment-1975051666
にあります。

Mew-debug を見ると、ユーザ名
`USER te...@example.com`
を送出せずにその手前で止まっていました。

なお、POP over TLS/SSL ではなくて
GnuTLS の STARTTLS を使って secure.plala.or.jp:110
へ接続しても同様の事象となります。

・最初の分析

POP の greeting は本来 1 行で送られてくるものなんでしょうが、
Mew-debug を見ると `<GREETING>` に複数行の CAPA レスポンスが入っていて、
しかもその最終行が `.` になっていませんでした。

secure.plala.or.jp:995 以外の正常動作する POP over TLS/SSL サーバがあったので、
同様の設定で試したところ、やはり `<GREETING>` に
複数行の CAPA レスポンスが入っていましたが、
最終行が `.` になっており、ユーザ名の送出へ進んでいました。

つまり、うまくいかない secure.plala.or.jp:995 は CAPA レスポンスを
2 つのパケットに分割して返してきていて、
1 つ目は

```
+OK Capability list follows
TOP
USER
RESP_CODES
PIPELINING
EXPIRE NEVER
UIDL
```

2 つ目は

```
.
```

となっています。時間差はほとんどなくて人間の目では
分割されていることがわからないぐらいです。

一方、うまくいく POP over TLS/SSL サーバは CAPA レスポンスを
1 パケットで分割せずに返していて、最終行が `.` になっていました。

・最初の修正案

最初に作った PR は単純に greeting は 1 行ではなくて複数行だとするものでした。
GnuTLS を使っていると上記のように複数行になるので良いのですが、
ご指摘いただいてそれ以外では 1 行しかないのでマズいと気が付きました。

・なぜ GnuTLS だと greeting が無いのか

元々
https://github.com/kazu-yamamoto/Mew/blob/d41dc93785d231f1e391ba61893aacd1331d5726/elisp/mew-pop.el#L777
の直前のコメントに書いてありましたが、GnuTLS を使うと greeting を受信できない、
先にクライアントから何か送出する必要がある、ということだと理解しています。

#正確には、open-network-stream 関数のリターン値から
# greeting を得ることは可能だが、
#受信データには入ってこないということだと思います。

これは POP over TLS/SSL であっても STARTTLS であっても同様という認識です。

# GnuTLS による STARTTLS は open-network-stream 関数の引数で、
#何を受信したら何を送信して、という手順を指定し、
# open-network-stream 関数の内部ですべて処理され、
# Mew へ返ってきた時には既に TLS アップグレードされた状態という認識です。
#また、Mew は open-network-stream 関数へ STARTTLS に失敗したら
#切断される設定をしているようなので、
# TLS アップグレードできなかったらエラーになると思います。

そのためとにかくダミーで CAPA を送信し、
そのレスポンスを greeting とみなして動作が継続できるようにしている、
と理解しました。

#本来は open-network-stream 関数のリターン値から greeting を得て
# greeting の処理をすべきなのかもしれませんが、
#受信データとして得られるわけではないので、
#ヤヤコシくなりそうに思います。

大抵の POP over TLS/SSL サーバは
この CAPA レスポンスを greeting とみなす workaround で大丈夫だったのでしょうが、
本来 1 行であるべきところ複数行になってしまっているのがよろしくないですし、
secure.plala.or.jp:995 は、さらにパケット分割されているため
引っ掛かってしまった、
ということだと思います。

・現在の PR

最初の修正は完全に破棄しました。

動作としては greeting を待つところから開始、
となっていたところを、GnuTLS を使っている場合は greeting 待ちをスキップして、
CAPA レスポンスを待つところから開始するようにしました。

元々、GnuTLS の場合は先に CAPA を送信するようになっていたので、
greeting を待たずにそのまま CAPA レスポンスを待てば普通に greeting ではなくて
CAPA レスポンスの複数行の処理が行われてそのまま処理が継続できる、
という形になっています。

最初の修正は GnuTLS 以外を考慮していませんでしたが、
今回の修正は GnuTLS 以外には影響がないので、特に問題は起きないものと思います。

GnuTLS で secure.plala.or.jp:995 以外に接続した場合であっても、
greeting は受信できず CAPA から始まることには変わりないですし、
CAPA レスポンスが複数行なのも同じですから問題ないものと思っています。

長々とすみません。
よろしくおねがいいたします。

---
細田 真道 <true...@trueroad.jp>

Hiroki Sato

unread,
Mar 6, 2024, 2:49:21 PM3/6/24
to true...@trueroad.jp, mew...@googlegroups.com, ka...@iij.ad.jp
佐藤です。

Masamichi Hosoda <true...@trueroad.jp> wrote
in <20240306.151952.13430...@trueroad.jp>:

tr> 動作としては greeting を待つところから開始、
tr> となっていたところを、GnuTLS を使っている場合は greeting 待ちをスキップして、
tr> CAPA レスポンスを待つところから開始するようにしました。

添付のように、ダミーのメッセージをプロセスフィルタに渡して、
無条件に FSM の遷移を発生させたほうが分かりやすいと思うのですが、
いかがでしょうか。これで secure.plala.or.jp のようなサーバでも
動作すると思います。

ご指摘のとおり、GnuTLS 対応を入れた時、場当たり的に greeting を
スキップする処理を入れてしまったことが原因です。
POP は greeting と capability の mulrep が異なるため、
greeting state で CAPA の応答を受け取ると、正常に動作しません。

PR にあるような inital state の変更は、ロジックが分かりにくくなるだけなので
避けるべきだと思います。

-- Hiroki
mew-fsm-fix-20240307-1.diff

Masamichi Hosoda

unread,
Mar 6, 2024, 5:45:54 PM3/6/24
to h...@allbsd.org, mew...@googlegroups.com, ka...@iij.ad.jp, true...@trueroad.jp
佐藤様

細田です。
ありがとうございます。

いただいたパッチで secure.plala.or.jp
POP over TLS/SSL, STARTTLS
両方とも動作しました。

佐藤さんに新しく PR を立てていただいて細田の PR #188 は取り下げるか、
細田の PR #188 を佐藤さんのパッチで置き換えるか、
どちらかにするのがよいかと思っていますが、
どうするのがよいでしょうか。

よろしくお願いいたします。
---
細田 真道 <true...@trueroad.jp>

Kazu Yamamoto

unread,
Mar 6, 2024, 9:44:43 PM3/6/24
to true...@trueroad.jp, h...@allbsd.org, mew...@googlegroups.com
山本です。

> 佐藤さんに新しく PR を立てていただいて細田の PR #188 は取り下げるか、
> 細田の PR #188 を佐藤さんのパッチで置き換えるか、
> どちらかにするのがよいかと思っていますが、
> どうするのがよいでしょうか。

調査&動作確認、ありがとうございます。
佐藤さんのクレジットが残る形であれば、どうのような方法でm構いません。
簡単なのは、commit メッセージに佐藤さんのクレジットを残し、
push -f していただくことです。

あと、github では、日本語でやりとして大丈夫です。

--
山本和彦


Masamichi Hosoda

unread,
Mar 7, 2024, 6:37:31 AM3/7/24
to ka...@iij.ad.jp, h...@allbsd.org, mew...@googlegroups.com, true...@trueroad.jp
山本様、佐藤様

細田です。
ありがとうございます。

佐藤さんのパッチを取り込みつつ、
GnuTLS から本物の greeting を得ることができたので、
ダミーの greeting ではなくて本物の greeting を渡すように変更するコミットを
追加してみました。

github の方にも日本語で書いております。

また、これとは別に IMAP でマーク変更が失われることがあるようでしたので、
別の PR #189 を立てています。

立て続けで申し訳ありませんが、
ご覧いただければ幸いです。

よろしくお願いいたします。

---
細田 真道 <true...@trueroad.jp>

Hiroki Sato

unread,
Mar 8, 2024, 1:53:58 PM3/8/24
to true...@trueroad.jp, ka...@iij.ad.jp, mew...@googlegroups.com
佐藤です。

Masamichi Hosoda <true...@trueroad.jp> wrote
in <20240307.203712.178696...@trueroad.jp>:

tr> 佐藤さんのパッチを取り込みつつ、
tr> GnuTLS から本物の greeting を得ることができたので、
tr> ダミーの greeting ではなくて本物の greeting を渡すように変更するコミットを
tr> 追加してみました。
tr>
tr> github の方にも日本語で書いております。

実際に受け取った connection greeting は、GnuTLS 対応パッチの初期から
意図的に使用していません。使うべきではない理由が 2 つありますので、
下記で説明します。

まず、STARTTLS を使う場合であれば、STARTTLS が成立した時点より前に得られた
通信内容は破棄すべき、というのが 1 つ目の理由です。

TLS が提供する機能はいくつかあります。その一つは通信内容の完全性です。
STARTTLS のように、同じトランスポート上で平文通信から
TLS に切り替わるような使い方をする場合、平文通信で交わされた内容は、
すべて信頼できないものとみなすのが原則です。
つまり、connection greeting は信頼できない情報です。
RFC 2595 や RFC 3207 でも、このような情報は MUST discard と
指示しています。

2 つ目の理由は、connection greeting に含まれる情報を使うケースは
ほとんどないことです。
STARTTLS ではなく、TCP 接続の最初から TLS のネゴシエーションを開始し、
TLS 通信路確立後に connection greeting を得ている場合は、
1 つ目の理由として指摘した問題は存在しません。

したがって、その時に限るようにコードを書くなら、GnuTLS の API にある
:greeting plist から情報を取り出して使っても問題はないでしょう。
しかし、取り出した情報は Mew の中で使っていませんから、
コードを追加しても得られる利益はほとんどないと思います。

-- Hiroki

Kazu Yamamoto

unread,
Mar 8, 2024, 8:05:13 PM3/8/24
to h...@allbsd.org, true...@trueroad.jp, mew...@googlegroups.com
山本です。

すいません。
まだ理解が完全でない状態での発言です。

> 2 つ目の理由は、connection greeting に含まれる情報を使うケースは
> ほとんどないことです。
> STARTTLS ではなく、TCP 接続の最初から TLS のネゴシエーションを開始し、
> TLS 通信路確立後に connection greeting を得ている場合は、
> 1 つ目の理由として指摘した問題は存在しません。

START TLS タイプのラッパーを作成する際、最初の平文での greeting は重要
です。(今回の細田さんの追加されたコードに関することではありません。)
なぜなら、START TLS は、セッションの「やり直し」がキモだからです。

https://salt.iajapan.org/wpmu/anti_spam/admin/tech/explanation/tls-arc/

状況がまだよく分かってないのですが、GnuTLSが「セッションのやり直し」と
して設計されてないのであれば、GnuTLSの方に問題があり、Mew で対処すべき
でないというのが、僕の直感的な感想です。

--
山本和彦


Masamichi Hosoda

unread,
Mar 8, 2024, 11:05:16 PM3/8/24
to ka...@iij.ad.jp, h...@allbsd.org, mew...@googlegroups.com, true...@trueroad.jp
細田です。

ひっかきまわすような感じかもしれず申し訳ありません。
細田の追加コードが不要であれば PR から外します。
細田が本物の greeting を GnuTLS から取得して使うようなコードを追加したのは、
山本さんからお示しいただいた上記の記事を読ませていただいたところ
記事中の「3. ラッパーとしての TLS」で「サーバの挨拶を保存する」
とお書きになっていた、ということと、
stunnel が平文でやりとりしていた本物の greeting を Mew へ返しているので、
それを真似たものです。

ただ、GnuTLS は stunnel と違ってラッパーではなく、
自力で TLS/SSL をしゃべるためのものと思えば
真似する必要はないのかもしれません。


次に、「セッションのやりなおし」についてですが、
GnuTLS の場合は、上記記事の「2. TLS」にある
「POP で TLS を利用する際のやりとり」で言うところの
`+OK Begin TLS negotiation` を受け取り TLS アップグレードが完了後に
Mew へ制御が戻ってくると理解しています。

この際、佐藤さんのパッチではダミーの greeting を
プロセスフィルタへ渡すことで Mew の制御を CAPA 送信へ進ませるようにしており、
細田の追加パッチはダミーではなくて GnuTLS が平文でやりとりしていた
本物の greeting 、つまり「POP で TLS を利用する際のやりとり」の
`+OK IIJ POP3 Server (pop.example.com) starting. <14663.12...@pop.example.com>`
を GnuTLS から受け取ってプロセスフィルタへ渡すように変更した、
ものです。

その先は暗号化された状態で改めて Mew が CAPA を送信し
サーバの実装機能の一覧を取得するところから「やりなおし」ているものと思います。


すると、TLS アップグレード前の greeting を破棄するか否かになります。
stunnel は破棄せずに再利用して Mew へ返しています。
ただし、平文で受け取っていた CAPA レスポンスは破棄しており、
Mew が「やりまおし」で再取得しています。

一応 RFC2595(IMAP, POP3 等)を読んでみましたが
「9. Security Considerations」の第 4 パラグラフから抜粋すると
「For this reason, clients MUST discard cached information about server
capabilities advertised prior to the start of the TLS handshake.」
と言っているので、CAPA レスポンスを破棄していれば十分で
greeting の破棄までは言及していないのかな、と思いました。

一方 RFC3207 (SMTP) は「6. Security Considerations」の第 6 パラグラフに
「For this reason, clients and servers MUST discard any knowledge
obtained prior to the start of the TLS handshake upon completion of
the TLS handshake.」
とあるので greeting も含めてすべて破棄せよと言っているようにも読めます。


最後に、平文でやりとりしていた本物の greeting は破棄すべき、
ということであれば PR から細田の追加コードを外します。
もしくは Mew-debug には本物の greeting を出力しつつ、
プロセスフィルタにはダミーの greeting を渡すようにしても
よいかもしれません。

また、今回再度コードを見直していたところ、
SMTP にも IMAP にしたような修正をした方がいいのかなと思いました。
こちらも IMAP 同様、ダミーの greeting を渡すように修正してもよいですし、
Mew-debug に本物の greeting を出力するようにしてもよいですし、
本物の greeting を渡すように修正してもよいです。


以上、長々と申し訳ありません。

---
細田 真道 <true...@trueroad.jp>

Kazu Yamamoto

unread,
Apr 26, 2024, 3:54:21 AM4/26/24
to mew...@googlegroups.com
山本です。

> 細田が本物の greeting を GnuTLS から取得して使うようなコードを追加したのは、
> 山本さんからお示しいただいた上記の記事を読ませていただいたところ
> 記事中の「3. ラッパーとしての TLS」で「サーバの挨拶を保存する」
> とお書きになっていた、ということと、
> stunnel が平文でやりとりしていた本物の greeting を Mew へ返しているので、
> それを真似たものです。

ようやく検証時間が取れたので、かなり時間をかけて検証しました。
https://github.com/kazu-yamamoto/Mew/pull/188
は、TLSプロトコルの設計思想に合致していることが確認できたので、
マージしました。

ありがとうございました。

--
山本和彦


Reply all
Reply to author
Forward
0 new messages