+ All Categories
Home > Documents > 超簡単!TELNETの話

超簡単!TELNETの話

Date post: 14-Jun-2015
Category:
Upload: ogatay
View: 10,164 times
Download: 4 times
Share this document with a friend
Popular Tags:
94
TELNETの話 第回 カーネル/VM探検隊 2013/4/13(Sat) @yogata 超簡単!
Transcript
Page 1: 超簡単!TELNETの話

TELNETの話第八回 カーネルVM探検隊

2013413(Sat)yogata

超簡単

ネットワークエンジニアの基本コマンド

telnet

後輩が来た時

先輩TELNETも

知らないんすかwwwwww

っていわれたらめちゃつらい

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 2: 超簡単!TELNETの話

ネットワークエンジニアの基本コマンド

telnet

後輩が来た時

先輩TELNETも

知らないんすかwwwwww

っていわれたらめちゃつらい

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 3: 超簡単!TELNETの話

telnet

後輩が来た時

先輩TELNETも

知らないんすかwwwwww

っていわれたらめちゃつらい

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 4: 超簡単!TELNETの話

後輩が来た時

先輩TELNETも

知らないんすかwwwwww

っていわれたらめちゃつらい

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 5: 超簡単!TELNETの話

先輩TELNETも

知らないんすかwwwwww

っていわれたらめちゃつらい

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 6: 超簡単!TELNETの話

っていわれたらめちゃつらい

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 7: 超簡単!TELNETの話

そこで

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 8: 超簡単!TELNETの話

TELNETを復復習してみた

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 9: 超簡単!TELNETの話

きょうのおだい 第一部TELNETを調べて分かったこと

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 10: 超簡単!TELNETの話

TELNETは暗号化出来る(uarr結論論)

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 11: 超簡単!TELNETの話

TELNETとは  みんな大好きRFC

  RFC 854 -‐‑‒ TELNET PROTOCOL SPECIFICATION  httptoolsietforghtmlrfc854

  RFC 855 -‐‑‒ TELNET OPTION SPECIFICATIONS  httptoolsietforghtmlrfc855

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 12: 超簡単!TELNETの話

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 13: 超簡単!TELNETの話

TELNETとは  TELNETの3つの役割 (RFC854)1   ldquoNetwork Virtual Terminalrdquoをつくること クライアントサーバはNVTへ入出力力する コードセット(7bit USASCII) 標準制御機能(割込出力力停止文字削除等)の定義などなど

2  セッション確立立時にオプション交換をすること ネゴシエーションフェーズを経てターミナルとプロセスは使う機能を決めることができる

3  ターミナルとプロセスで表示内容を同期させること ターミナルの情報をプロセスに随時通知する 例例えばtelnet上でエディタを開くときにターミナルはプロセスにターミナルサイズを伝えたいはず(viの論論理理行行を決めるにはターミナルサイズが必須)

これなに

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 14: 超簡単!TELNETの話

TELNETネゴシエーション サーバとクライアントで使用する機能を決めるNAME13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 CODE13 13 13 13 13 13 13 13 13 13 13 13 13 13 MEANING13 SE13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 24013 13 13 13 End13 of13 subnegotiation13 parameters13 SB13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25013 13 13 13 Indicates13 that13 what13 follows13 is13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 subnegotiation13 of13 the13 indicated13 option13 WILL13 (option13 code)13 13 25113 13 13 13 Indicates13 the13 desire13 to13 begin13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 performing13 or13 confirmation13 that13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 you13 are13 now13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 WONT13 (option13 code)13 25213 13 13 13 Indicates13 the13 refusal13 to13 perform13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 continue13 performing13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DO13 (option13 code)13 13 13 13 25313 13 13 13 Indicates13 the13 request13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 perform13 or13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 confirmation13 that13 you13 are13 expecting13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 the13 other13 party13 to13 perform13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 indicated13 option13 DONT13 (option13 code)13 25413 13 13 13 Indicates13 the13 demand13 that13 the13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 other13 party13 stop13 performing13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 or13 confirmation13 that13 you13 are13 no13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 longer13 expecting13 the13 other13 party13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 to13 perform13 the13 indicated13 option13 IAC13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 13 25513 13 13 13 Data13 Byte13 25513

使うオプションの宣言or DOに対する合意

使わないオプションの宣言or DONʼrsquoTに対する合意相手に使って欲しいオプションの宣言or WILLに対する合意相手に使ってほしくないオプションの宣言or WONʼrsquoTに対する合意

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 15: 超簡単!TELNETの話

もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは

helliphellipでも独自実装はいやだなあ

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 16: 超簡単!TELNETの話

TELNETの暗号化  TELNETの暗号化はRFC化されている (RFC2946〜~)  RFC2946 -‐‑‒ Telnet Data Encryption Option

 暗号化オプションネゴシエーションのコマンドコード暗号化アルゴリズムをネゴシエーションするコードの定義

 TELNETコマンドコード38 (0x26)  RFC2947以降降は各暗号化アルゴリズムに関する定義

 暗号化アルゴリズムのネゴシエーションはSuBnegotiation(0xfa)の中で行行う

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 17: 超簡単!TELNETの話

パケットヘッダではパケット上では [IAC] [Command] [OpCode]の組みが複数連続

コマンドコードWILL 251(0xfb)WONʼrsquoT 252(0xfc)DO 253(0xfd)DONʼrsquoT 254(0xfe)IAC 255(0xff)

オプションコードAuthentication 37(0x25)Encryption 38(0x26)Suppress Go Ahead 3(0x09)Terminal Type 24(0x18)

など

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 18: 超簡単!TELNETの話

パケットヘッダではサブネゴシエーションの場合は[IAC] [SB] [OpCode] [OpType(オプションコード依存)] [IAC] [SE]の順番になる

コマンドコードSE(Subnegotiation End)

240(0xf0)SB(SuBnegotiation)

250(0xfa)IAC 255(0xff)

オプションコードAuthentication 37(0x25)

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 19: 超簡単!TELNETの話

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る

 第三部telnetを見見る

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 20: 超簡単!TELNETの話

TELNETのキャプチャを見見る 環境

  サーバFreeBSDのデフォルトのtelnetd  クライアントFreeBSDのデフォルトのtelnet telnet localhost

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 21: 超簡単!TELNETの話

シーケンス概要(1)サーバ クライアント

No8 [IAC] [SB] [Auth] [IS] [RSA] [Auth][IAC] [SB] [Enc] [Beginstart][IAC] [SB] [Enc] [Support] [typeshellip]hellip

No4 オプションネゴシエーション [IAC] [WillDo] がたくさん

No5 Do Authentication Optionだけ回答

No7 残りのオプションすべてに回答[IAC] [SB] [Auth] [Send] [RSA][IAC] [SB] [Enc] [Support] [typeshellip] hellip

No9 [IAC] [SB] [Auth] [Reply] [RSA] [Auth]

No11 [IAC] [SB] [Auth] [IS] [RSA] [Reject]

No12 [IAC] [SB] [Auth] [Reply] [RSA] [Accept]

最初のネゴシーエションSuppress Go Aheadとか単発でネゴシエーションが終わるのはここで完了了

認証方式のネゴシエーション

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 22: 超簡単!TELNETの話

シーケンス概要(2)サーバ クライアント

No17 [IAC] [SB] [Enc] [Reply] [DES_CFB64][IAC] [SB] [Enc] [ENC_KEYID]

No15 [IAC] [SB] [Auth] [Reply] [RSA] [Forward][IAC] [SB] [Enc] [IS] [DES_CFB64]

No16 [IAC] [SB] [Enc] [IS] [DES_CFB64][IAC] [WONʼrsquoT] [Envoption]

No18 [IAC] [SB] [Enc] [ENC_KEYID][IAC] [SB] [Enc] [DEC_KEYID]

No19 [IAC] [SB] [Enc] [DEC_KEYID][IAC] [SB] [Encrypt-‐‑‒Start]

No20 [IAC] [SB] [Enc] [Start]

No14 [IAC] [SB] [Auth] [IS] [RSA] [Response]

No21以降降 コンソールに表示させるデータ

引き続き認証方式と暗号化方式のネゴシエーション

暗号化方式のネゴシエーション

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 23: 超簡単!TELNETの話

telnet(のsrac)書いた人が認証フローはこうだよって文章化していたので確かめてみたらそのとおりだったhelliphellip

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 24: 超簡単!TELNETの話

Host1 Host2

IAC DO ENCRYPT

IAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64 CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

暗号化のシーケンス (DEC_CFB64の場合) 参考RFC29522946暗号化は方向があるので双方向でシーケンスが発生する(rarr見見た目2倍)

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 25: 超簡単!TELNETの話

No4クライアントからサーバへオプションのネゴシエーションを行行う

Do 相手(今回はサーバ)に使って欲しいオプション

Will 自分(今回はクライアント)が使いたいオプション

認証暗号化全二重通信端末タイプの指定

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 26: 超簡単!TELNETの話

No5サーバは認証にだけ Do で答える(なぜか)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 27: 超簡単!TELNETの話

No7サーバからクライアントへ最初のネゴシエーションに返答する(すべてDoWillで返す)

(参考)No41

2 3 4 _5 6 7 8 9 10

2 3 4 _5 6 7 8 9 10

1

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 28: 超簡単!TELNETの話

No7認証のネゴシエーションSRAでの認証を提案

認証方式の提案(SEND)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 29: 超簡単!TELNETの話

No7暗号化のネゴシエーション利利用可能な暗号化方式のリストを通知する

暗号化方式の提案(SUPPORT)サーバrarrクライアントの方向

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 30: 超簡単!TELNETの話

No8クライアントからサーバへ合意したオプションについて詳細なパラメータを決めていく

暗号化方式の提案(SUPPORT)クライアントrarrサーバの方向

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 31: 超簡単!TELNETの話

No8の認証のところ公開鍵を送る

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 32: 超簡単!TELNETの話

No8の暗号化のところサポートしている暗号化方式を送る

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 33: 超簡単!TELNETの話

No9RSA認証方式のネゴシーエションをする

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 34: 超簡単!TELNETの話

No11REJECTされる仕様のUSER(1)がwiresharkでREJCT(1)と解釈したためだと考えられる

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 35: 超簡単!TELNETの話

No12認証方式のネゴシエーション

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 36: 超簡単!TELNETの話

No14認証方式の決定

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 37: 超簡単!TELNETの話

No15認証方式の決定と暗号化方式の決定(まずは認証方式)

Host1 Host2IAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 38: 超簡単!TELNETの話

No15認証方式の決定と暗号化方式の決定(次に暗号化方式)

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 39: 超簡単!TELNETの話

No16暗号化アルゴリズムの決定

1 Command Names and Codes Encryption Type DES_CFB64 1 Suboption Commands CFB64_IV 1 CFB64_IV_OK 2 CFB64_IV_BAD 3

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 40: 超簡単!TELNETの話

No17暗号化アルゴリズムの決定と鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 41: 超簡単!TELNETの話

No18鍵交換

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 42: 超簡単!TELNETの話

No19鍵交換と暗号化開始(片方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 43: 超簡単!TELNETの話

No20暗号化して通信開始(これで双方向)

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT REQUESTSTART IAC SE

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 44: 超簡単!TELNETの話

0x0030 8tL^Free0x0040 BSDamd64(sage 0x0050 osakaremu)(pts0x0060 3)Last 0x0070 loginMonApr 0x0080 8234455from 0x0090 localhostFreeB0x00a0 SD90-RELEASE( 0x00b0 GENERIC)0Tue 0x00c0 Jan3074630 0x00d0 UTC2012Youh 0x00e0 avemail

0x0030 KHsrltd 0x0040 C 0x0050 jr 0x0060 M`4ue 0x0070 uur 0x0080 aAcU= 0x0090 mn4Q 0x00a0 A(1mb 0x00b0 Yg[~HT 0x00c0 5txampO 0x00d0 TM 0x00e0 E01-C^n6

with ENCRYPTION option without ENCRYPTION option

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 45: 超簡単!TELNETの話

結論論

TELNETは暗号化できるし公開鍵暗号で認証もできる

TELNETは平文でしか通信できない

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 46: 超簡単!TELNETの話

ちょっとクイズ

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 47: 超簡単!TELNETの話

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 48: 超簡単!TELNETの話

Q ldquoff fa 26 01 02 ff f0rdquoはなんでしょう

A  1 IAC SB ENCRYPT SUPPORT DES_CFB64 IAC SE

2 IAC SB AUTH SEND SRA IAC SE

3 IAC SB AUTH REPLY SRA CONTINUE IAC SE

よくわからない

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 49: 超簡単!TELNETの話

telnetの気持ちになって考えてみる

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 50: 超簡単!TELNETの話

きょうのおだい 第一部TELNETを調べて分かったこと

  TELNETは暗号化出来る

 第二部TELNETを見見る 鍵認証できるし暗号化も出来る でも認証化方式ネゴの途中でREJECTされる(なぜか)

 第三部telnetを見見る

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 51: 超簡単!TELNETの話

main()

tninit()

tn()

TerminalSaveState()

command()

getopt()

ringについてringhで定義される構造体Ringの変数リングバッファと管理理変数ネットワークからrecv()したデータやネットワークへsend()するデータ等を保持バッファバッファサイズリングバッファなので次の受信位置今処理理している位置のポインタ等

mainc

sys_bsdc

commandc

commandc

main()mainc

引数が有る場合はこちらを実行行tn()内で無限ループするのでtn()からcommand()には結果的に行行かない (詳細は次ページ)

引数がない場合はこちらを実行行

init_termnal()rarrttyiringttyoringの初期化init_network()rarrnetiringnetoringの初期化init_telnet()rarrシェル環境変数を取得(env_init())エスケープ文字等の設定などinit_sys()rarr標準入出力力を入出力力に設定(tin=stdin tout=stdout)

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 52: 超簡単!TELNETの話

tn()tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquoかldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

認証暗号化の初期化TELNETオプションの初期ネゴシエーション設定(send_do(option 1) send_will(option 1))rarr netoringに ldquoIAC DOWILL [option]rdquo を追加する (まだ送信しない)

netoringの未送信データnetiringの未処理理データttyoringの未表示データnetiringの未処理理入力力データがあるか確認する(ring_full_count() ring_empty_count())

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 53: 超簡単!TELNETの話

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main()mainc

rarr見見てきた所

rarrこれから見見る所

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 54: 超簡単!TELNETの話

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

sys_bsdc

commandc

commandc

main()

次はココ

rarr見見てきた所

rarrこれから見見る所mainc

mainc

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 55: 超簡単!TELNETの話

process_ring()process_ring()sys_bsdc

recv()

TerminalRead()

netflush()

ttyflush()

戻り値の処理理

[ネットワークからの受信]recv()で受信したデータをnetiringに読み込むnetdata設定が有効ならダンプ表示する

[ターミナルからの入力力]tin(=stdin)をttyiringに読み込むtermdata設定が有効ならダンプ表示する

[ネットワークへの送信]暗号化が有効であれば暗号化する送信バッファの内容をサーバへ送信する(send())

[ターミナルへの出力力]tout(=stdout)に出力力(write())する

netflush()ttyflush()したバイト数を戻り値に設定する

sys_bsdc

networkc

terminac

tintoutは最初の方のinit_sys()でtin=stdin tout=stdout してある

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 56: 超簡単!TELNETの話

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 57: 超簡単!TELNETの話

telrcv()telrcv()telnetc

decrypt_input()

switch(telrcv_state)

TS_DATA

TS_CR

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

改行行処理理

IACを受け取ればTS_IACへ遷移それ以外は受信データをttyoringへ追加する

TS_IAC

WILLWONTDODONT

SB

IAC状態なら受信データに応じて状態を遷移する

TS_XXXに遷移する

サブオプションバッファを初期化して(IAC SBは入れない)TS_SBに遷移する

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 58: 超簡単!TELNETの話

telrcv()switch(telrcv_state)

TS_WONT

telnetcのtelrcv()のところ

willoption()

dontoption()

TS_WILL

wontoption()

dooption()TS_DO

TS_DONT

DO FLOW CONTROLのときは端末の再設定を行行う

この4つはいずれの場合も最後にTS_DATAに遷移する

encrypt_send_support()暗号化は後回し

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 59: 超簡単!TELNETの話

telrcv()switch(telrcv_state)telnetcのtelrcv()のところ

TS_SE

IAC IAC

IAC SE

IAC IACでもIAC SEでもない

サブオプション処理理をしてTS_IACに戻す

サブオプションバッファにIACを追加してTS_SBに戻す

IAC SEをサブオプションバッファに追加してサブオプション処理理をしてTS_DATAに戻す

suboption()

suboption()

次々のページ

次々のページ

TS_SB IACならTS_SEにしてIAC IACかIAC SEを判断するIAC以外ならサブオプションバッファに追加する

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 60: 超簡単!TELNETの話

telrcv()switch(telrcv_state)

ring_consumed()amp戻り値の処理理

telnetcのtelrcv()のところ

netiringの受信データを処理理したのでリングバッファの処理理受信位置のポインタを更更新する処理理したバイト数を返す

netiringに未処理理のデータがあれば最初に戻る

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 61: 超簡単!TELNETの話

telrcv()suboption()telnetc

サブオプションバッファはIAC SBの後のオプションコードから入っているのでオプションコードに応じた処理理を行行う使いたくないオプションには同意しない必要に応じて返答を作成しnetoringに追加する

TELOPT_TTYPE

TELOPT_TSPEED

TELOPT_LFLOW

TELOPT_LINEMODE

IAC SB TTYPE IS [termname] IA SEをnetoringに追加する

IAC SB TSPEED IS [ospeed] [ispeed] IA SEをnetoringに追加する

ターミナルを再設定

続きは次のページ

MODEFORWARDMASKSLC

IAC SB LINEMODE MODE ACK IAC SEをnetoringに追加する

必要に応じて IAC SB LINEMODE DONʼrsquoT FORWARDMASKをnetoringに追加する

なんか色々

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 62: 超簡単!TELNETの話

telrcv()suboption()telnetc

TELOPT_NEW_ENVIRON

TELOPT_XDISPLOC

env_opt()

env_opt_start() ENVIRON用バッファを確保してIAC SB ENV IS を入れる

続きは次のページ

env_opt_start()して環境変数の区切切り毎にenv_opt_add()する

env_opt_add() ENVオプション用バッファ(opt_reply)追加する

DISPLAY変数がNULLだったり異異常に長かったりしたらWONTにする(下のメッセージが作れないから)使える場合は応答をnetoringに追加するIAC SB XDISPLOC IS $DISPLAY IAC SE

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 63: 超簡単!TELNETの話

telrcv()telnetc

TELOPT_AUTHENTICATION

ISSENDREPLY

NAME

suboption()

auth_is()auth_send()auth_reply()

auth_name()

authc (Authenticator)-‐‑‒gtis()(Authenticator)-‐‑‒gtsend()(Authenticator)-‐‑‒gtreply()

ユーザ名を取得

続きは後のページ

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 64: 超簡単!TELNETの話

認証について

typedef struct XauthP int type int wayint (init)(struct XauthP int)int (send)(struct XauthP )void (is)(struct XauthP unsigned char int)void (reply)(struct XauthP unsigned char int)int (status)(struct XauthP char int)void (printsub)(unsigned char int unsigned char int)

Authenticator

Authenticator authenticators[] = hellipifdef SRA AUTHTYPE_SRA AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY sra_init sra_send

telnetがサポートする認証方式はXauthP構造体(Authenticator型)をリスト化したauthenticators変数に設定している

初期化SENDISREPLY時の応答ステータス確認デバッグ表示が関数ポインタになっている

uuml  XauthPの詳細 (authh)

uuml  authenticatorsにSRAを設定しているようす (authc) sra_is sra_reply sra_status sra_printsub endif

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 65: 超簡単!TELNETの話

SRAの場合キャプチャからSRAであることがわかるのでこれに着目して確認するtelnetc

TELOPT_AUTHENTICATION

IS

SEND

REPLY

suboption()

auth_is()

auth_send()

auth_reply()

authc

(Authenticator)-‐‑‒gtis()

(Authenticator)-‐‑‒gtsend()

(Authenticator)-‐‑‒gtreply()

sra_is()

sra_send()

sra_reply()

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

Server -gt Client は SEND REPLY だけなので IS は省略

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 66: 超簡単!TELNETの話

SRAの場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

サポートしているすべての認証方式で(Authenticator)-‐‑‒gtinit()rarr sra_init()

そういえばtelnet()で認証暗号化の初期化をしていた

sra_init()

応答のひな形をldquoIAC SB AUTH IS SRArdquoで初期化

鍵認証のペアを作成genkey()

pkc

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 67: 超簡単!TELNETの話

SRAの場合sra_send()srac

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE

公開鍵を送るldquoIAC SB AUTH IS SRA KEY ltPKAgt IAC SErdquo

サブオプション解析でIAC SB AUTH SENDを受信していた時に実行行する

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 68: 超簡単!TELNETの話

SRAの場合sra_reply()

SRA_KEY

srac

鍵暗号化された共通鍵ltPKBgtを抽出

ltPKBgtと秘密鍵(PKA)から共通鍵を取得する

ldquoIAC SB AUTH REPLY SRA KEY ltPKBgt IAC SErdquo

受信データの解析

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

ユーザ名を共通鍵で暗号化するltPKA(U)gt

IAC SB AUTH IS SRA USERltPKA(U)gt IAC SE

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 69: 超簡単!TELNETの話

SRAの場合

SRA_CONTINUE ldquoIAC SB AUTH REPLY SRA CONTINUE IAC SErdquo

Client ServerIAC DO AUTH

IAC WILL AUTH

IAC SB AUTH SEND SRA IAC SEIAC SB AUTH IS SRAKEY ltPKAgt IAC SE

IAC SB AUTH REPLY SRAKEY ltPKBgt IAC SE

IAC SB AUTH IS SRAUSERltPKA(U)gt IAC SEIAC SB AUTH REPLY SRACONTINUE IAC SE

IAC SB AUTH IS SRAPASS ltPKA(P)gt IAC SE

IAC SB AUTH REPLY SRA[ACCEPT|REJECT] IAC SE続きは次のページ

パスワードを共通鍵で暗号化するltPKA(P)gt

ldquoIAC SB AUTH IS SRA USER ltPKA(P)gtrdquo

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 70: 超簡単!TELNETの話

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

SUPPORT

REQSTART

REQEND

encrypt_support()

encrypt_request_start()

encrypt_request_end()

encryptc

STARTEND

encrypt_start()

encrypt_end()

復復号用の関数ポインタを設定

復復号用の関数ポインタを削除

IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgtのうち最初にサポートしている方式を採用

encrypt_start_output()

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encrypt_send_end()

IAC SB ENCRYPT END IAC SEをnetoringに書き込み暗号化用の関数ポインタを削除(0にする)

続きは次ページ

(Encryptions)-‐‑‒gtstart()

戻ってtelnetcのsuboption()の続き

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 71: 超簡単!TELNETの話

telrcv()telnetc

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

REQSTARTの時と同じ

IAC SB ENCRYPT START [key] IAC SEをnetoringに書き込み暗号化用の関数ポインタを設定

encryptc

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 72: 超簡単!TELNETの話

暗号化についてtelnetがサポートする暗号化方式はEncryptions構造体(Encryptions型)をリスト化したencryptions変数に設定している

typedef struct const char name int typevoid (output)(unsigned char int)int (input)(int)void (init)(int)int (start)(int int)int (is)(unsigned char int)int (reply)(unsigned char int)void (session)(Session_Key int)int (keyid)(int unsigned char int )void (printsub)(unsigned char int unsigned char int)

Encryptions

static Encryptions encryptions[] = DES_CFB64 ENCTYPE_DES_CFB64 cfb64_encrypt cfb64_decrypt cfb64_init cfb64_start

認証のAuthenticatorsと同様に関数ポインタになっている

uuml  Encryptionsの詳細 (encrypth)

uuml  encryptionsに設定しているようす (authc) cfb64_is cfb64_reply cfb64_session cfb64_keyid cfb64_printsub

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 73: 超簡単!TELNETの話

DES_CFB64の場合キャプチャからDEC_CFB64であることがわかるのでこれに着目して確認する

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

未処理理の受信データが無くなるまで処理理する

暗号化が有効であれば復復号する

データ処理理は現在のステータス(telrcv_state)に応じて変わるため場合分けする

willoption()TS_WILL

encrypt_send_support()後回しにしたアレ

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 74: 超簡単!TELNETの話

TELOPT_ENCRYPT

suboption()

ISREPLY

ENC_KEYIDDEC_KEYID

encrypt_is()encrypt_reply()

encrypt_enc_keyid()encrypt_dec_keyid()

(Encryptions)-‐‑‒gtis()(Encryptions)-‐‑‒gtreply()

encrypt_keyid()

encrypt_start_output()

encryptc

TS_SE

cfb64_start()

SUPPORT encrypt_support() (Encryptions)-‐‑‒gtstart()

cfb64_is()

cfb64_reply()

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 75: 超簡単!TELNETの話

DES_CFB64の場合telnet()

auth_encrypt_init()

auth_init()

encrypt_init()

そういえばtelnet()で認証暗号化の初期化をしていた

fb64_init()応答のひな形を設定IAC SB ENCRYPT IS DES_CFB64

enc_desc

応答のひな形を設定 [X] [X] [X] [X] DES_CFB64厳密にはENCRYPT_DES_CFB64

SUPPORTメッセージを作成IAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

サポートしているすべての暗号化方式で(Encryptions)-‐‑‒gtinit() rarr cfb64_init()

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 76: 超簡単!TELNETの話

DES_CFB64の場合

encryptc

telrcv()telnetc

decrypt_input()

switch(telrcv_state)

willoption()TS_WILL

encrypt_send_support()

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

さっき作ったSUPPORTメッセージをnetoringに書き込むIAC SB ENCRYPT SUPPORT ltencrypt-‐‑‒listgt IAC SE

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 77: 超簡単!TELNETの話

DES_CFB64の場合cfb64_start()

fb64_start()

DIR_ENCRYPT

DIR_DECRYPT

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

stateがFAILEDならIN_PROGRESSにする

DESのIVを作る

送信データの作成IAC SB ENCRYPT IS DES_CFB64 CFB64_IV ltIVgt IAC SE

netoringに書き込む

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 78: 超簡単!TELNETの話

DES_CFB64の場合cfb64_is()

fb64_is()

それ以外

FB64_IS

fb64_stream_iv()

netoringに書き込む

エラー表示

送信データの作成IAC SB ENCRYPT REPLY DES_CFB64 FB64_IV_OK IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 79: 超簡単!TELNETの話

DES_CFB64の場合cfb64_reply()

fb64_reply()

FB64_IV_BAD

FB64_IV_OK

fb64_stream_iv()

encrypt_send_keyid()

fb64_stream_iv

それ以外

エラー表示

netoringに書き込む

送信データの作成IAC SB ENCRYPT ENCRYPT_ENC_KEYID 0 IAC SE

Host1 Host2IAC DO ENCRYPTIAC WILL ENCRYPT

IAC SB ENCRYPT SUPPORTDES_CFB64 IAC SE

IAC SB ENCRYPT IS DES_CFB64CFB64_IV ltIVgt IAC SE

IAC SB ENCRYPT REPLY DES_CFB64CFB64_IV_OK IAC SE

IAC SB ENCRYPT START IAC SE

IAC SB ENCRYPT ENC_KEYIDkeyid IAC SE

IAC SB ENCRYPT DEC_KEYIDkeyid IAC SE

enc_desc

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 80: 超簡単!TELNETの話

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

長かった

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 81: 超簡単!TELNETの話

telsnd()telsnd()telnetctelnetc

netoringの空きある

ttyiringの中身をnetoringに処理理ampコピーする(send()しない)

command()制御文字

BINARYモード 改行行をエスケープ

y

nn

y

IAC(0xff)ならIAC IACをnetoringに追加(0xffは0xff 0xffにエスケープが必要)それ以外ならそのままnetoringに追加

ttyiringから1バイト取得

未処理理ttyiring全部処理理した

もう一回確認しても未処理理無し(ring_full_consecutive())

した

まだ あった

ある

処理理したバイト数を戻り値にして終了了

なかった

無い

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 82: 超簡単!TELNETの話

telnetクライアント

telnet()

Scheduler()

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc

tninit()

tn()

TerminalSaveState()

command()

getopt()

mainc

sys_bsdc

commandc

commandc

main() mainc

次はココ

rarr見見てきた所

rarrこれから見見る所

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 83: 超簡単!TELNETの話

command()command(int top const char tbuf int cnt)commandc

tbuf == NULL 標準入力力から1行行読み込む

作業領領域にコピー

y

n 引数無しでtelnetを実行行したときはこっちに来る

入力力コマンドを探すgetcmd()

ldquoAmbiguous commandrdquo (Command)-‐‑‒gthandler() ldquoInvalid commandrdquo

完全一致 or部分一致が1つ

部分一致が2つ以上

部分一致完全一致が1つもない

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 84: 超簡単!TELNETの話

telnetクライアントコマンドtelnetクライアント上のコマンドはCommand構造体でリスト化して保持している(cmdtab変数)

typedef struct const char name command name const char help help string (NULL for no help) int (handler)(int char ) routine which executes command int needconnect Do we need to be connected to execute

Command

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

各コマンドで行行される内容は関数ポインタになっている

uuml  Command構造体の詳細 (commandc)

uuml  encryptionsに設定しているようす (authc)

(Command)-‐‑‒gthandler()

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 85: 超簡単!TELNETの話

telnetクライアントコマンド

telnet telnetgt open 19216811

static Command cmdtab[] = close closehelp bye 1 logout logouthelp (int ()(int char ))logout 1 display displayhelp display 0 mode modestring modecmd 0 telnet openhelp tn 0 open openhelp tn 0 quit quithelp (int ()(int char ))quit 0 send sendhelp sendcmd 0 hellip

(Command)-‐‑‒gthandler()

telnet 19216811

引数無しなのでcommand()にいって openコマンドのハンドラtn()で処理される

引数ありなのでtn()で処理される

同じ

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 86: 超簡単!TELNETの話

まとめ

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 87: 超簡単!TELNETの話

TELNETは超簡単

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 88: 超簡単!TELNETの話

(参考)  TELNETがソースルーティングするところ

  コマンドとパケットキャプチャから動作を確認する

tn()

telnet()

Scheduler()

commandc

telnetc

telnetc

process_ring()

telrcv()

telsnd()

sys_bsdc

telnetc

telnetc次ページ以降降

ホスト名の解析ldquordquoから始まっていたらソケットrdquordquo or ldquordquo から始まっていたらrdquordquoかrdquordquoで区切切ってソースルーティングサーバに接続する

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 89: 超簡単!TELNETの話

Loose Source Routing

gt telnet 1033110444105551066610777

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 90: 超簡単!TELNETの話

Strict Source Routing

gt telnet 1033110111102221033310444

  ホスト名をʼrsquoʼrsquoから初めてʼrsquoʼrsquo区切切りにするとLoose Source RoutingのIPオプション付きパケットを送信する

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 91: 超簡単!TELNETの話

しくみtn()

引数からソケット(ʻlsquoʼrsquo)かソースルート(ʻlsquoʼrsquo か ʻlsquoʼrsquo)かホスト名かを判断する

if (hostp[0] == ) (ソケットだった時の処理) else if (hostp[0] == || hostp[0] == )

if ((hostname = strrchr(hostp )) == NULL) hostname = strrchr(hostp ) if (hostname == NULL) hostname = hostp else hostname++ srcroute = 1

else hostname = hostp

ソースルートならsourceroute()する

if (srcroute = 0) hellip

result = sourceroute(res hostp ampsrp ampsrlen ampproto ampopt)

作ったソースルートのIPオプションヘッダを設定する

if (srp ampamp setsockopt(net proto opt (char )srp srlen) lt 0)

hostpからソースルートのIPオプションヘッダを作ってsrpに入れる

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 92: 超簡単!TELNETの話

sourceroute()

引数をチェック(長さが不不正に短くない

こととか)

ソースルーティングのIP Option作成ʼrsquoʼrsquo から始まっていたら SSRRを設定ʼrsquoʼrsquoから始まっていたらLSRRを設定それ以外だったら終了了する

初期化してないlenp (tn()ではampsrlen)に対して if(lenp lt 7) return -‐‑‒1してる気がするhelliphellip

pointer(=4)を埋めるホスト名をʼrsquoʼrsquoとʼrsquoʼrsquoで区切切って埋めるソースルーティングのIP Option長を決めてlength埋める

if (cp == ) cp++ lsrp++ = IPOPT_SSRR

else lsrp++ = IPOPT_LSRR

if (cp = )

return -1

詳細は次ページ

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る

Page 93: 超簡単!TELNETの話

gt telnet 1033110111102221033310444

cpの初期位置は ホスト名の先頭rsquo1rsquo (lsquorsquoの次)

for (c = 0) for (cp2 = cp (c = cp2) cp2++) if (c == ) cp2++ = 0 if (cp2 == ) cp2++ else if (c == ) cp2++ = 0rsquo else if (c == ) cp2++ = 0 else continue break error = getaddrinfo(cp NULL amphints ampres) _sin = (struct sockaddr_in )res-gtai_addr memcpy(lsrp (char )amp_sin-gtsin_addr 4) lsrp += 4 cp = cp2

lsquorsquoとrsquorsquoの区切り位置を探して 区切り位置をrsquo0にする rarr次にgetaddrinfo()に渡すため

IP Optionに埋める

次を探す

区切りのホスト名から ソースルーティングのIPオプション付きパケットを作る


Recommended