Hatena::Grouprubyist

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

Ruby ロゴ (C) Ruby Association LLC

2011年09月08日(木)

SSH サーバを作る(その20) ユーザ認証プロトコル(2)

| 22:06 | SSH サーバを作る(その20) ユーザ認証プロトコル(2) - Going My Ruby Way を含むブックマーク はてなブックマーク - SSH サーバを作る(その20) ユーザ認証プロトコル(2) - Going My Ruby Way SSH サーバを作る(その20) ユーザ認証プロトコル(2) - Going My Ruby Way のブックマークコメント

公開鍵認証

認証メソッド "publickey" は公開鍵認証です。

公開鍵アルゴリズムは任意のものを選択します。(実質的な選択肢は DSS か RSA です)

トランスポート層プロトコルの鍵交換で選択したサーバホスト鍵のアルゴリズムとは無関係に選択できます。

サーバは、

  • ユーザの提示した公開鍵がユーザのものであるか
  • その公開鍵で署名の検証が問題ないか*1

をチェックします。

gmrw-ssh2-server ではユーザ管理をしていないので、公開鍵の登録などもありません。なので、公開鍵がユーザのものであるかのチェックは行っていません。


認証メソッド "publickey"(署名なし) の場合

ユーザは署名なしのリクエストを送信する場合があります。

これは、サーバがその公開鍵アルゴリズムをサポートしているかどうかの事前確認のために行われます。

  <client>                         <server>
    |  <USERAUTH_REQUEST(50)>        |
    |------------------------------->|
    |        [with_signature=false]  |
    |                                | 
    |        <USERAUTH_FAILURE(51)>  |
    |<-------------------------------|(※認証NG、その公開鍵アルゴリズムは
    |                                |           サポートされていない)
    |                                |
    |                                |
    |           <USERAUTH_PK_OK(60)> |
    |<-------------------------------|(※リクエストの公開鍵での
    |                                |   検証が可能な場合)
    |                                |
USERAUTH_REQUEST (C->S)
user_name
ユーザ名です
service_name
サービス名です
method_name
認証メソッド名です。この場合、"publickey"です
with_signature
署名有無フラグです。この場合 false です
key_algorithm_name
鍵アルゴリズム名です。'ssh-dss' や 'ssh-rsa' などです
key_blob
公開鍵です。SSH 鍵形式でエンコーディングされてます

以下は参考です。gmrw-ssh2-server での SSH 鍵形式をパックするコードです。

(RSA 鍵形式)
      GMRW::SSH2::Field.pack [:string, 'ssh-rsa'],
                             [:mpint,  e        ],
                             [:mpint,  n        ]

(DSA 鍵形式)
      GMRW::SSH2::Field.pack [:string, 'ssh-dss' ],
                             [:mpint,  p         ],
                             [:mpint,  q         ],
                             [:mpint,  g         ],
                             [:mpint,  pub_key   ]

USERAUTH_FAILURE (S->C)
auths_can_continue
サーバが継続可能な認証メソッドのリストです(例:["password","publickey"])
USERAUTH_PK_OK (S->C)
key_algorithm_name
USERAUTH_REQUEST で送られた鍵アルゴリズム名です
key_blob
USERAUTH_REQUEST で送られた公開鍵です

認証メソッド "publickey"(署名あり) の場合

  <client>                         <server>
    |  <USERAUTH_REQUEST(50)>        |
    |------------------------------->|
    |        [with_signature=true,   |
    |              signature=署名]   |
    |                                | 
    |        <USERAUTH_FAILURE(51)>  |
    |<-------------------------------|(※認証NG)
    |                                |               
    |                                |
    |        <USERAUTH_SUCCESS(52)>  |
    |<-------------------------------|(※認証OK)
    |                                | 
    |                                |
USERAUTH_REQUEST (C->S)
user_name
ユーザ名です
service_name
サービス名です
method_name
認証メソッド名です。この場合、"publickey"です
with_signature
署名有無フラグです。この場合 true です
key_algorithm_name
鍵アルゴリズム名です。'ssh-dss' や 'ssh-rsa' などです
key_blob
公開鍵です。SSH 鍵形式でエンコーディングされてます
signature
以下のエンコードされたデータに対する、公開鍵(key_blob)のペア私有鍵による署名です
(署名対象データ。gmrw-ssh2-server のコード抜粋)
                                [:string,  session_id                 ],
                                [:byte,    message[:type             ]],
                                [:string,  message[:user_name        ]],
                                [:string,  message[:service_name     ]],
                                [:string,  message[:method_name      ]],
                                [:boolean, message[:with_pk_signature]],
                                [:string,  message[:pk_algorithm     ]],
                                [:string,  message[:pk_key_blob      ]]))


USERAUTH_FAILURE (S->C)
auths_can_continue
サーバが継続可能な認証メソッドのリストです(例:["password","publickey"])
USERAUTH_SUCCESS (S->C)

パラメータはありません。


gmrw-ssh2-server でのコード

gmrw-ssh2-server 実装の公開鍵認証部分のコードです。

  def authenticate(message)
    algo = message[:pk_algorithm]
    blob = message[:pk_key_blob]
    sig  = message[:with_pk_signature] && message[:pk_signature]

    debug( "publickey auth: algorithm = #{algo}" )
    debug( "publickey auth: sig       = #{sig}" )

    key = SSH2::Algorithm::HostKey.algorithms[algo].create(blob) rescue nil
    debug( "publickey auth: key       = #{key}" )

    ok = key && sig && key.unpack_and_verify(sig, SSH2::Field.pack(
                                                    [:string,  session_id                 ],
                                                    [:byte,    message[:type             ]],
                                                    [:string,  message[:user_name        ]],
                                                    [:string,  message[:service_name     ]],
                                                    [:string,  message[:method_name      ]],
                                                    [:boolean, message[:with_pk_signature]],
                                                    [:string,  message[:pk_algorithm     ]],
                                                    [:string,  message[:pk_key_blob      ]]))
    debug( "publickey auth: #{ok}" )

    ok   ? welcome(message) :
    sig  ? please_retry     :
           send_message(:userauth_pk_ok, :pk_algorithm => algo,
                                         :pk_key_blob  => blob)
  end

認証が成功したら

認証が成功すると要求していたサービス(通常、'ssh-connection')が認可されます。

*1:ユーザが私有鍵を有するか、が判断できます