Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年07月30日(土)

SSHサーバを作る (その0) 下調べ

| 16:10 |  SSHサーバを作る (その0) 下調べ - Going My Ruby Way を含むブックマーク はてなブックマーク -  SSHサーバを作る (その0) 下調べ - Going My Ruby Way  SSHサーバを作る (その0) 下調べ - Going My Ruby Way のブックマークコメント

SSH サーバを作ります。

目的は、

  • SSH プロトコルを理解するためのプロトタイプ作り
  • 今迄書いてきた Ruby 小技を試す

です。

終点まで長ーくかかると思うので、気長にやっていきます。


以下、下調べのメモです。

SSH とは

SSH は Secure Shell (セキュア・シェル)のことです。

telnetftp が実現してきた機能のセキュアな置き替えにもなります。

機能 従来 SSH (セキュア・シェル)
シェル telnet ssh
ファイル転送 ftp scp, sftp

SSH の実装

OpenSSH
メジャーなオープンソースの実装。Linux など UNIXOSCygwin(Windows) などで動作する。サーバとクライアントを含む。
Ruby/SSH
Ruby による実装。SSH クライアント。

SSH の仕様

SSH2(SSH バージョン2) は RFC で仕様化されています。*1

この他にも SSH に関連する RFC があります。

和訳としては以下のサイトが有用です。

SSH の機能

SSH プロトコルは 3 層構造であり、それぞれ以下の機能を提供します。

位置プロトコル提供する機能
最下層トランスポート層プロトコル*2気密性(暗号化)、完全性、サーバ認証、圧縮
中間層認証プロトコルクライアント認証(ユーザ認証)
最上層コネクションプロトコル多重化チャネル
トランスポート層プロトコル
気密性(暗号化)、完全性、サーバ認証、圧縮などの機能を提供する。これらの機能および鍵交換のアルゴリズム(ないしスキーム)は特定でなく通信時に選択する。
認証プロトコル
クライアント認証(ユーザ認証)の機能を提供する。スキームは特定でなく通信時に選択する。
コネクションプロトコル
多重化チャネルで一つ接続の中で仮想的に複数の接続があるかのように振る舞う。チャネルにはタイプがあり、タイプごとに異なるサービスを実現する。

SSH プロトコルの概要

前提

SSH プロトコルは 8ビット透過な転送路上で動作します。

通常は TCP を使用します。サーバが使用する標準ポート番号は 22 です。

下位層プロトコル ポート番号
TCP 22
バージョン情報の交換

SSH プロトコルは最初にバージョン情報の交換を行ないます。

バージョン情報は以下のような文字列です。

SSH-2.0-billsSSH_3.6.3q3

サーバとクライアントで互いのバージョンのネゴが取れたら、バイナリパケットによる通信に移行します。(以後、ずっとバイナリパケットによる通信です)

データ型

SSH プロトコルには以下のデータ型があります。

説明
byte任意の8ビットの値(octet)
byte[n]byte型の配列。n はbyte型データの数
boolean単一のbyteに格納される真理値。0 が 偽(false)。1 が 真(true)。0 以外の値はすべて真と解釈する
uint3232ビット符号無し整数。NBO*3 で格納される
uint6464ビット符号無し整数。NBO で格納される
string任意の長さの文字列。string形式(詳細は後述)で格納される
mpint2の補数で表現される多倍精度整数。string 形式で格納される
name-list「,」(カンマ)で区切られた name のリスト。string 形式で格納される。name は「,」を含んではならず、長さが 0 であってはならない

string 形式は、以下のような形式です。*4

フィールド名説明
uint32length 'data' の byte 数
byte[n]data 実データ。n は 'length' で示される

string 型 の "Ruby" は以下のようになります。

  00  00  00  04  52  75  62  79   # HEX
  (長さ = 4)      R   u   b   y

name-list 型の ["Ruby","SSH"] は以下のようになります。

  00  00  00  09  52  75  62  79  2c  53  53  48  # HEX
  (長さ = 9)      R   u   b   y   ,   S   S   H

いずれの場合も、長さ = 0 の場合は以下のようになります。

  00  00  00  00
  (長さ = 0)

mpint 型は 2 の補数表現であり、8ビットずつ MSB*5を先頭にして格納します。

  00  00  00  02  12  34       # mpint 型の 0x1234
  00  00  00  02  ed  cc       # mpint 型の -(0x1234)

MSB がセットされる正の数の場合は 0x00 を先行させます。

MSB がクリアされる負の数の場合は 0xff を先行させます。

先行バイト(0x00 や 0xff)は不要の場合は含めてはいけません。

  00  00  00  03  00  80  00   # mpint 型の 0x8000
                  ^^
                  先行バイト(これがないと 80 00 は負数と解釈される)
  00  00  00  03  ff  41  11   # mpint 型の -(0xbeef)
                  ^^ 
                  先行バイト(これがないと be ef は正数と解釈される)

mpint 型の 0 は長さ = 0 の string 形式です。

  00  00  00  00               # mpint 型の 0

1 バイトは厳密に 8 ビット(= 1 オクテット)ではありませんが、本稿では、これ以後「1 バイト = 8 ビット」として話を進めます。

バイナリパケット

パケットは以下の形式です。

フィールド名説明暗号化MAC圧縮
uint32packet_length'packet_length'自体 と 'mac' を含まないパケット長(byte 単位)
bytepadding_length'padding' の長さ(byte単位)
byte[n1]payload実データ。n1 = 'packet_length' - 'padding_length' - 1
byte[n2]paddingランダムなbyte値によるパディング。n2 = 'padding_length'(詳細は別途)
byte[m]macMAC*6。m は MAC アルゴリズムで決まる。初期は m = 0
メッセージ

パケットの 'payload' にはメッセージが格納されます。

メッセージの最初のフィールドは以下のようになっています。

フィールド名説明
bytetypeメッセージ番号
... ... (以降のフィールドはメッセージ毎に定義されている)

メッセージ番号はメッセージの種類を示します。以下のようにカテゴライズされています。

カテゴリ番号説明
トランスポート層プロトコル 1 .. 19 トランスポート層一般
20 .. 29 アルゴリズムのネゴシエーション
30 .. 49 鍵交換方式ごとに特有(再利用可能)
認証プロトコル 50 .. 59 ユーザ認証一般
60 .. 79 ユーザ認証法ごとに特有(再利用可能)
コネクション・プロトコル 80 .. 89 コネクションプロトコル一般
90 .. 127 チャネルに関連したメッセージ
クライアントプロトコルのための予約 128 .. 191 予約
ローカルな拡張 192 .. 255 ローカルな拡張

再利用可能と説明されているカテゴリは、メッセージ番号に対応するメッセージがアルゴリズムやスキームによって決まります。


以上、おおざっぱに説明しましたが、詳細は RFC を参照してください。


実装について

依存性

Ruby のバージョンには依存しないように作るつもりですが、以下の環境で開発します。

ライブラリの使用

標準添付ライブラリは活用します。特に openssl は使用します。

OpenSSLSSL プロトコルを使用するためのライブラリです。SSH では SSL は使用しませんが、OpenSSL にバンドルされている暗号化スイートなどを利用します。

その他のサードパーティ製のライブラリは使用しない予定です。


参考書籍

SSH の全般的なことが書かれています。実装についてもほんの少し触れています。

暗号技術のことが分かりやすく書いてあります。(私が所持しているのは旧版です)

新版暗号技術入門 秘密の国のアリス

新版暗号技術入門 秘密の国のアリス

OpenSSL ライブラリの使い方について書かれています。

OpenSSL―暗号・PKI・SSL/TLSライブラリの詳細―

OpenSSL―暗号・PKI・SSL/TLSライブラリの詳細―

*1:個人的には、4251(概要) -> 4253(最下層プロトコル) -> 4252(中間層) -> 4254(最上層) の順が読み易いと思います。4250(名前、番号) は傍用です。

*2OSI 7階層モデルでいうトランスポート層ではありません

*3:Network Byte Order。ビッグエンディアンでの格納順

*4:説明の便宜上の形式です。RFC に示されているわけでありません

*5:Most Significant Bit。最上位ビット

*6:Message Authentication Code。メッセージ認証コード