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オプション付きパケットを作る
ネットワークエンジニアの基本コマンド
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オプション付きパケットを作る
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オプション付きパケットを作る
後輩が来た時
先輩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オプション付きパケットを作る
先輩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オプション付きパケットを作る
っていわれたらめちゃつらい
そこで
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オプション付きパケットを作る
そこで
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オプション付きパケットを作る
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オプション付きパケットを作る
きょうのおだい 第一部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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
もしかしてネゴシエーションで暗号化機能を合意すれば使えるのでは
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オプション付きパケットを作る
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オプション付きパケットを作る
パケットヘッダではパケット上では [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オプション付きパケットを作る
パケットヘッダではサブネゴシエーションの場合は[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オプション付きパケットを作る
きょうのおだい 第一部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オプション付きパケットを作る
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オプション付きパケットを作る
シーケンス概要(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オプション付きパケットを作る
シーケンス概要(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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
結論論
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オプション付きパケットを作る
ちょっとクイズ
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
きょうのおだい 第一部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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
telnetクライアント
telnet()
Scheduler()
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
telnetクライアント
telnet()
Scheduler()
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
認証について
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
暗号化について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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
telnetクライアント
telnet()
Scheduler()
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オプション付きパケットを作る
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オプション付きパケットを作る
telnetクライアント
telnet()
Scheduler()
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
まとめ
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オプション付きパケットを作る
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オプション付きパケットを作る
(参考) 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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る
しくみ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オプション付きパケットを作る
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オプション付きパケットを作る
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オプション付きパケットを作る