GSSAPI との SSPI/Kerberos 相互運用性
GSSAPI との相互運用性要件である場合は、Kerberos セキュリティ サポート プロバイダー (SSP) を使用する際に注意が必要です。 次のコード規則を使用すると、GSSAPI ベースのアプリケーションとの相互運用性を実現できます。
サンプル コードは、プラットフォーム ソフトウェア開発キット (SDK) の Samples\Security\SSPI\GSS にあります。 さらに、同等の UNIX サンプルは、MIT および Heimdal Kerberos ディストリビューション、GSS クライアント、およびサーバーで配布されます。
Windows と互換性のある名前
GSSAPI 関数では、RFC で指定されている gss_nt_service_name という名前形式が使用されます。 たとえば、sample@host.dom.com は GSSAPI ベースのアプリケーションで使用できる名前です。 Windows オペレーティング システムでは、gss_nt_service_name 形式は認識されず、sample/host.dom.com@REALM などの完全な サービス プリンシパル名を使用する必要があります。
認証
通常、認証は、クライアントとサーバーの間の接続が最初に設定されるときに処理されます。 このサンプルでは、クライアントは セキュリティ サポート プロバイダー インターフェイス (SSPI) を使用しており、サーバーは GSSAPI を使用しています。
SSPI クライアントで認証を設定する方法
- AcquireCredentialsHandle を使用して、送信資格情報を取得します。
- gss_import_name() でサービス名を作成し、gss_acquire_cred を使用して受信資格情報を取得します。
- InitializeSecurityContext (Kerberos) を使用して、サーバーに送信する認証トークンを取得します。
- トークンをサーバーに送信します。
GSSAPI サーバーで認証を設定する方法
クライアントからのメッセージを解析して、セキュリティ トークンを抽出します。 gss_accept_sec_context 関数を使用して、トークンを引数として渡します。
サーバーからのメッセージを解析して、セキュリティ トークンを抽出します。 このセキュリティ トークンを InitializeSecurityContext (Kerberos) に渡します。
クライアントに応答トークンを送信します。
gss_accept_sec_context 関数は、クライアントに返信できるトークンを返すことができます。
続行する必要がある場合は、応答トークンをサーバーに送信します。それ以外の場合は、認証のセットアップは完了です。
続行する必要がある場合は、クライアントからの次のトークンを待ちます。それ以外の場合は、認証のセットアップは完了です。
メッセージの整合性とプライバシー
ほとんどの GSSAPI ベースのアプリケーションでは、GSS_Wrap 関数を使用してメッセージを送信する前に署名します。 逆に、GSS_Unwrap 関数は署名の検証を行います。 GSS_Wrap はバージョン 2.0 の API で使用でき、現在では広く使用されており、プロトコルにセキュリティを追加するための GSSAPI の使用について説明するインターネット標準に明記されています。 以前は、GSS SignMessage および SealMessage 関数が、メッセージの整合性とプライバシーのために使用されていました。 GSS_Wrap と GSS_Unwrap は、整合性とプライバシーの両方に使用され、プライバシーの使用は "conf_flag" 引数の値によって制御されます。
gss_get_mic 関数と gss_verify_mic 関数を使用するように GSSAPI ベースのプロトコルが指定されている場合、正しい SSPI 関数は MakeSignature と VerifySignature になります。 MakeSignature と VerifySignature は GSS_Wrap (conf_flagが 0 に設定されている場合)、または GSS_Unwrap と相互運用できないことに注意してください。 署名のみに設定された EncryptMessage (Kerberos) と gss_verify_mic を混在する場合にも、同じことが当てはまります。
Note
GSS_Wrap および GSS_Unwrap が呼び出される場合は、MakeSignature または VerifySignature 関数を使用しないでください。
GSS_Wrap と同等の SSPI は、EncryptMessage (Kerberos) で、整合性とプライバシーの両方に対応しています。
次の例は、EncryptMessage (Kerberos) を使用して、GSS_Unwrap によって検証されるデータに署名する方法を示しています。
SSPI クライアントで以下を実行します。
// Need three descriptors, two for the SSP and
// one to hold the application data.
in_buf_desc.cBuffers = 3;
in_buf_desc.pBuffers = wrap_bufs;
in_buf_desc.ulVersion = SECBUFFER_VERSION;
wrap_bufs[0].cbBuffer = sizes.cbSecurityTrailer;
wrap_bufs[0].BufferType = SECBUFFER_TOKEN;
wrap_bufs[0].pvBuffer = malloc(sizes.cbSecurityTrailer);
// This buffer holds the application data.
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].cbBuffer = in_buf.cbBuffer;
wrap_bufs[1].pvBuffer = malloc(wrap_bufs[1].cbBuffer);
memcpy(wrap_bufs[1].pvBuffer, in_buf.pvBuffer, in_buf.cbBuffer);
wrap_bufs[2].BufferType = SECBUFFER_PADDING;
wrap_bufs[2].cbBuffer = sizes.cbBlockSize;
wrap_bufs[2].pvBuffer = malloc(wrap_bufs[2].cbBuffer);
maj_stat = EncryptMessage(&context,
SignOnly ? KERB_WRAP_NO_ENCRYPT : 0,
&in_buf_desc, 0);
// Send a message to the server.
GSSAPI サーバーで以下を実行します。
// Received message is in recv_buf.
maj_stat = gss_unwrap(&min_stat, context, &recv_buf, &msg_buf,
&conf_state, (gss_qop_t *) NULL);
(void) gss_release_buffer(&min_stat, &recv_buf);
// Original message is in msg_buf.
GSS_Unwrap と同等の SSPI は、DecryptMessage (Kerberos) です。 以下は、DecryptMessage (Kerberos) を使用して、GSS_Wrap によって暗号化されたデータを復号化する方法を示す例です。
GSSAPI サーバーで以下を実行します。
// Seal the message.
send_buf.value = msg;
send_buf.length = msglen;
// If encrypt_flag = 1, privacy; encrypt_flag = 0, integrity.
maj_stat = gss_wrap(&min_stat, context, encrypt_flag,
GSS_C_QOP_DEFAULT, &send_buf, &state, &msg_buf);
// The message to send is in msg_buf.
SSPI クライアントで以下を実行します。
wrap_buf_desc.cBuffers = 2;
wrap_buf_desc.pBuffers = wrap_bufs;
wrap_buf_desc.ulVersion = SECBUFFER_VERSION;
// This buffer is for SSPI.
wrap_bufs[0].BufferType = SECBUFFER_STREAM;
wrap_bufs[0].pvBuffer = xmit_buf.pvBuffer;
wrap_bufs[0].cbBuffer = xmit_buf.cbBuffer;
// This buffer holds the application data.
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].cbBuffer = 0;
wrap_bufs[1].pvBuffer = NULL;
maj_stat = DecryptMessage(
&context,
&wrap_buf_desc,
0, // no sequence number
&qop
);
// This is where the data is.
msg_buf = wrap_bufs[1];
// Check QOP of received message.
// If QOP is KERB_WRAP_NO_ENCRYPT, the message is signed only;
// otherwise, it is encrypted.